Is there a way to disable adding properties into a class from an instance of the class?
Perhaps you can implement __set()
and throw an exception from there:
class a {
private $v1;
public $v2;
public function __set($name, $value) {
throw new Exception("Cannot add new property \$$name to instance of " . __CLASS__);
}
}
Prevent creating new attributes outside __init__
I wouldn't use __dict__
directly, but you can add a function to explicitly "freeze" a instance:
class FrozenClass(object):
__isfrozen = False
def __setattr__(self, key, value):
if self.__isfrozen and not hasattr(self, key):
raise TypeError( "%r is a frozen class" % self )
object.__setattr__(self, key, value)
def _freeze(self):
self.__isfrozen = True
class Test(FrozenClass):
def __init__(self):
self.x = 42#
self.y = 2**3
self._freeze() # no new attributes after this point.
a,b = Test(), Test()
a.x = 10
b.z = 10 # fails
PHP: deterring instance variable creation outside the class
Using magic methods (namly __set
) you can tell the class "if this is not set in here, ignore it", for instance;
<?php
class Resource {
private $test;
public function __construct() {
echo "in init";
$this->test = "new val";
}
public function __set($name, $val)
{
// If this variable is in the class, we want to be able to set it
if (isset($this->{$name})
{
$this->{$name} = $val;
}
else
{
// Do nothing, add an error, anything really
}
}
}
$resource = new Resource();
$resource->foo = "bar";
echo $resource->foo; // Will print nothing
For reference, please see the guide
How to prevent setting instance attribute directly?
Python doesn't really have "private" variables like C++. Even if you set the variable as private by using a the _
prefix from PEP8 (as you have done). You can always access the variable using A._x
.
If you want to emulate private variables for some reason, you can use __
(double underscore) prefix as it mangles the name. Similarly you can use @property
decorator to make a getter and setter function and prevent access to the attribute. However, even these fail as you can access the variables directly using __dict__
.
So the Pythonic way is to leave it the way it is. Python is like perl in this respect. To paraphrase a famous line about privacy from the Perl book,
the philosophy is that you should stay out of the living room because
you weren't invited, not because it is defended with a shotgun.
How to remove a property from class at run time
I was not able to REMOVE the property, I was trying to create a dynamic JSON, with 2 different classes merged together but without some properties (not needed for that merged class), so what I did was, I added a custom attribute and added to field/properties which I didn't need, and used reflection to create a custom JSON at runtime after merging 2 classes.
How to restrict setting an attribute outside of constructor?
The usual way is to use a "private" attribute starting with an underscore, and a read-only property for public access:
import operator
class Person(object):
def __init__(self, name, ssn):
self.name = name
self._ssn = ssn
ssn = property(operator.attrgetter("_ssn"))
Note that this does not really hinder anybody to change the attribute _ssn
, but the leading _
documents that the attribute is private.
Why does getting a class property from an instance raise an AttributeError?
That is due to the way attribute lookup works. Upon trying to retrieve an
attribute in an instance, Python does:
call the instance's class
__getattribute__
(not the metaclass__getattribute__
), which in turn will:Check the instance's class, and its superclasses following the method resolution order, for the attribute. It does not proceed to the class of the class (the metaclass) - it follows the inheritance chain..
- if the attribute is found in the class and it has a
__get__
method, making it a descriptor: the__get__
method is called with the instance and its class as parameters - the returned value is used as the attribute value- note: for classes using
__slots__
, each instance attribute is recorded in a special descriptor - which exists in the class itself and has a__get__
method, so instance attributes for slotted classes are retrieved at this step
- note: for classes using
- if there is no
__get__
method, it just skips the search at the class.
- if the attribute is found in the class and it has a
check the instance itself: the attribute should exist as an entry in the instances
__dict__
attribute. If so, the corresponding value is returned. (__dict__
is an special attribute which is accessed directly in cPython, but would otherwise follow the descriptor rule for slotted attributes, above)The class (and its inheritance hierarchy) are checked again for the attribute, this time, regardless of it having a
__get__
method. If found, that is used. This attribute check in the class is performed directly in the class and its superclasses__dict__
, not by calling their own__getattribute__
in a recursive fashion. (*)
The class (or superclasses)
__getattr__
method is called, if it exists, with the attribute name. It may return a value, or raise AttributeError(__getattr__
is a different thing from the low level__getattribute__
, and easier to customize)AttributeError is raised.
(*) This is the step that answers your question: the metaclass is not searched for an attribute in the instance. In your code above, if you try to use A.cls_prop
as a property, instead of A().cls_prop
it will work: when retrieving an attribute directly from the class, it takes the role of "instance" in the retrieval algorithm above.
(**) NB. This attribute retrieval algorithm description is fairly complete, but for attribute assignment and deletion, instead of retrieval, there are some differences for a descriptor, based on whether it features a __set__
(or __del__
) method, making it a "data descriptor" or not: non-data descriptors (such as regular methods, defined in the instance's class body), are assigned directly on the instance's dict, therefore overriding and "switching off" a method just for that instance. Data descriptors will have their __set__
method called.
how to make properties defined in the metaclass work for instances:
As you can see, attribute access is very customizable, and if you want to define "class properties" in a metaclass that will work from the instance, it is easy to customize your code so that it works. One way is to add to your baseclass (not the metaclass), a __getattr__
that will lookup custom descriptors on the metaclass and call them:
class Base(metaclass=Meta):
def __getattr__(self, name):
metacls = type(cls:=type(self))
if hasattr(metacls, name):
metaattr = getattr(metacls, name)
if isinstance(metaattr, property): # customize this check as you want. It is better not to call it for anything that has a `__get__`, as it would retrieve metaclass specific stuff, such as its __init__ and __call__ methods, if those were not defined in the class.
attr = metaattr.__get__(cls, metacls)
return attr
return super().__getattr__(name)
and:
In [44]: class A(Base):
...: pass
...:
In [45]: a = A()
In [46]: a.cls_prop
Out[46]: True
Related Topics
PHP Warning: PHP Startup: Unable to Load Dynamic Library 'Pdo_Mysql.So'
PHP Float Calculation 2 Decimal Point
Codeigniter Redirect -- the Uri You Submitted Has Disallowed Characters
PHP Array, Are Array Indexes Case Sensitive
Redirect to 'Www' Before Ssl Requirement
Flex/Grid Properties Are Deleted in Gmail Email
Laravel 5.3 - How to Add Sessions to 'Api' Without Csrf
How to Make My PHP Script Run at a Certain Time Everyday
MySQL Stored Function to Create a Slug
Pdoexception' with Message 'Sqlstate[22001]: String Data, Right Truncated: 0
Setting a Cookie in an Ajax Request
How to Send Email with PDF Attachment Using PHP
Change Cart Item Prices Based on Custom Cart Item Data in Woocommerce