In general, this is not a good idea for the reasons that @yak mentioned in his comment. You are basically preventing the user from supplying valid arguments that have the correct attributes/behavior but are not in the inheritance tree you hard-coded in. Disclaimer aside, there are a few of options available for what you are trying to do. The main issue is that there are no private attributes in Python. So if you just have a plain old object reference, say self._a ,
you can not guarantee that the user won't set it directly even though you have provided a setter that does type checking for it. The options below demonstrate how to really enforce the type checking. Override __setattr__This method will only be convenient for a (very) small number of attributes that you do this to. The __setattr__ method is what gets called when you use dot notation to assign a regular attribute. For example, class A:
def __init__(self, a0):
self.a = a0
If we now do A().a = 32 , it would call A().__setattr__('a', 32)
under the hood. In fact, self.a = a0 in __init__ uses self.__setattr__ as well. You can use this to enforce the type check: class A:
def __init__(self, a0):
self.a = a0
def __setattr__(self, name, value):
if name == 'a' and not isinstance(value, int):
raise TypeError('A.a must be an int')
super().__setattr__(name, value)
The disadvantage of this method is that you have to have a separate if name == ... for each type you want to check (or if name in ... to check multiple names for a given type). The advantage is that it is the most straightforward way to make it nearly
impossible for the user to circumvent the type check. Make a propertyProperties are objects that replace your normal attribute with a descriptor object (usually by using a decorator). Descriptors can have __get__ and __set__ methods that customize how the underlying attribute is accessed. This is sort of like taking the corresponding if branch in __setattr__ and putting it into a method that will run just for that attribute. Here is an example: class A:
def __init__(self, a0):
self.a = a0
@property
def a(self):
return self._a
@a.setter
def a(self, value):
if not isinstance(value, int):
raise TypeError('A.a must be an int')
self._a = value
A slightly
different way of doing the same thing can be found in @jsbueno's answer. While using a property this way is nifty and mostly solves the problem, it does present a couple of issues. The first is that you have a "private" _a attribute that the user can modify directly, bypassing your type check. This is almost the same problem as using a plain getter and setter, except that now a is accessible as the "correct" attribute that redirects to the setter behind the scenes, making it
less likely that the user will mess with _a . The second issue is that you have a superfluous getter to make the property work as read-write. These issues are the subject of this question. Create a True Setter-Only DescriptorThis solution is probably the most robust overall. It is suggested in the accepted answer to the question
mentioned above. Basically, instead of using a property, which has a bunch of frills and conveniences that you can not get rid of, create your own descriptor (and decorator) and use that for any attributes that require type checking: class SetterProperty:
def __init__(self, func, doc=None):
self.func = func
self.__doc__ = doc if doc is not None else func.__doc__
def __set__(self, obj, value):
return self.func(obj, value)
class A:
def __init__(self, a0):
self.a = a0
@SetterProperty
def a(self, value):
if not isinstance(value, int):
raise TypeError('A.a must be an int')
self.__dict__['a'] = value
The setter stashes the actual value directly into the __dict__ of the instance to avoid recursing into itself indefinitely. This makes it possible to get the attribute's value without supplying an explicit getter. Since the descriptor a does not
have the __get__ method, the search will continue until it finds the attribute in __dict__ . This ensures that all sets go through the descriptor/setter while gets allow direct access to the attribute value. If you have a large number of attributes that require a check like this, you can move the line self.__dict__['a'] = value into the descriptor's __set__ method: class ValidatedSetterProperty:
def __init__(self, func, name=None, doc=None):
self.func = func
self.__name__ = name if name is not None else func.__name__
self.__doc__ = doc if doc is not None else func.__doc__
def __set__(self, obj, value):
ret = self.func(obj, value)
obj.__dict__[self.__name__] = value
class A:
def __init__(self, a0):
self.a = a0
@ValidatedSetterProperty
def a(self, value):
if not isinstance(value, int):
raise TypeError('A.a must be an int')
Update Python3.6 does this for you almost out-of the box:
https://docs.python.org/3.6/whatsnew/3.6.html#pep-487-descriptor-protocol-enhancements TL;DRFor a very small number of attributes that need type-checking, override __setattr__ directly. For a larger number of attributes, use the setter-only descriptor as shown above. Using properties directly for this sort of application introduces more problems than
it solves.
How do you define a variable in Python class?
In Python, Class variables are declared when a class is being constructed. They are not defined inside any methods of a class because of this only one copy of the static variable will be created and shared between all objects of the class.
Can you define a variable in a class?
A class variable is an important part of object-oriented programming (OOP) that defines a specific attribute or property for a class and may be referred to as a member variable or static member variable.
Can you specify variable type in Python?
Specify a Variable Type
Casting in python is therefore done using constructor functions: int() - constructs an integer number from an integer literal, a float literal (by rounding down to the previous whole number), or a string literal (providing the string represents a whole number)
What are the types of variables in class in Python?
Inside a class, we can have three types of variables.. Instance variables (object level variables). Static variables (class level variables). Local variables..
|