python property and inheritance [duplicate] - python

class human(object):
def __init__(self, name=''):
self.name = name
#property
def name(self):
return self._name
#name.setter
def name(self, value):
self._name = value
class superhuman(human):
#property
def name(self):
return 'super ' + name
s = superhuman('john')
print s.name
# Doesn't work :( "AttributeError: can't set attribute"
s.name = 'jack'
print s.name
I want to be able to override the property but be able to use the super parent's setter without having to override the setter in the child class.
Is that pythonicaly possible?

Use just the .getter decorator of the original property:
class superhuman(human):
#human.name.getter
def name(self):
return 'super ' + self._name
Note that you have to use the full name to reach the original property descriptor on the parent class.
Demonstration:
>>> class superhuman(human):
... #human.name.getter
... def name(self):
... return 'super ' + self._name
...
>>> s = superhuman('john')
>>> print s.name
super john
>>> s.name = 'jack'
>>> print s.name
super jack
The property descriptor object is just one object, even though it can have multiple methods associated with it (the getter, setter and deleter). The .getter, .setter and .deleter decorator functions provided by an existing property descriptor return a copy of the descriptor itself, with that one specific method replaced.
So in your human base class what happens is that you first create the descriptor with the #property decorator, then replace that descriptor with one that has both a getter and a setter with the #name.setter syntax. That works because python decorators replace the original decorated function with the same name, it basically executes name = name.setter(name). See How does the #property decorator work? for the details on how that all works.
In your subclass you simply use that trick to create a new copy of the descriptor with just the getter replaced.

Related

#property decorator of python not working

I was trying to set some property to a class via decorator but its not working as expected. How can I get the age via property decorator.
class Person:
def __init__(self):
self.name = ""
self.age = ""
self.dob = ""
#property
def name(self):
return self._name
#name.setter
def name(self, value):
self._name = value
#property
def age(self):
return self._age
#age.setter
def age(self, value):
self._age = value
#property
def dob(self):
return self._dob
#dob.setter
def dob(self, value):
self._dob = value
self._age = 20 #Utility.getAge(value)
if __name__ == '__main__':
p = Person()
p.name = "Andrew"
p.dob = "10-10-1980"
print p.name
print p.dob
print p.age
Output:
John
10-10-1980
#20 <-missing
I am not getting the age. Am I missing something?
Ok, this took me a while to actually find out why the above code was not working in python 2.7.
If you look at the property documentation for python2.7, you would find that the class that has the property decorators used is actually inheriting object class and your code doesn't.
Now, when you don't inherit, the property decorator actually doesn't work and setting or getting properties don't work either
(Put a print statements in getter or setter functions and they wont be printed since they were never invoked while setting p.name or getting p.name).
Question : So how come get/set for p.name and p.dob works?
Since, you are not inheriting object class in your class, the property decorators are useless, they are not being invoked but have created those property on the Person object.
But, when you use below code, you are explicitly setting those value (without the use of setters), hence thy are printed and p.age never got assigned any value.
p.name = "Andrew"
p.dob = "10-10-1980"
Code Fix : Update your class declaration to -->
class Person(object):
and setters/getters would work (check using print statements) and self.age would also work.
Bonus : Python3 onwards, all classes, by default, inherit object class.

Python Lazy Loading Property not working for Class method

I have a class Foo that uses lazy loading for Foo.bar.
class Foo(object):
#property
def bar(self):
if not hasattr(self, '_bar'):
self._initBar()
return self._bar
def _initBar(self):
self._bar = 'bar'
foo = Foo()
print(foo.bar) # prints "bar"
However when i try to convert Foo to use class methods only, Foo.bar is not giving me bar, but instead giving:
<property object at 0x000001CA1C344728>
Why is it not giving me bar?
class Foo(object):
#property
#classmethod
def bar(cls):
if not hasattr(cls, '_bar'):
cls._initBar()
return cls._bar
#classmethod
def _initBar(cls):
cls._bar = 'bar'
print(Foo.bar) # prints "<property object at 0x000001CA1C344728>"
The property built-in is a handy tool in Python that presents an easy use case of a more powerful mechanism, which is the "descriptor protocol".
Basically, any object when retrieved from an instance or from a class is first checked if it has one of __get__, __set__ or __del__ methods. property wraps getter functions to be called by __get__ when the attribute is retrieved from an instance, but returns the property object itself when it is retrieved from the class instead. (That is even a common use case for other descriptors)
Thus, if you want a property like behavior for class attributes, you have to create your own descriptor class, sporting the __get__ method - or, simply create your class with a metaclass, and use property as is, on the metaclass. The drawbacks of the later are many: you willneed one custom metaclass for each class where you want the managed class attributes being just the first of them. On the other hand, creating your own descriptor is quite easy:
class MyProperty:
def __init__(self, initializer):
self.initializer = initializer
def __set_name__(self, owner, name):
self.name = name
def __get__(self, instance, owner):
if not hasattr(owner, "_" + self.name):
initializer = getattr(owner, self.initializer)
initializer()
return getattr(owner, "_" + self.name)
class Foo:
bar = MyProperty("_initBar")
#classmethod
def _initBar(cls):
cls._bar = 'bar'
Please note that __set_name__ is only implemented from Python 3.6 on. On older Python's, including Python 2.x, you should use:
class MyProperty(object):
def __init__(self, initializer, name):
self.initializer = initializer
self.name = name
def __get__(self, instance, owner):
if not hasattr(owner, "_" + self.name):
initializer = getattr(owner, self.initializer)
initializer(owner)
return getattr(owner, "_" + self.name)
class Foo(object):
bar = MyProperty("_initBar", name='bar')
#classmethod
def _initBar(cls):
cls._bar = 'bar'
You could use a metaclass, because property objects are meant to be accessed through the instance and if your intended instance is the class itself, then you need to put the property on the class of the class, i.e. the metaclass:
In [37]: class MetaFoo(type):
...: #property
...: def bar(cls):
...: if not hasattr(cls, '_bar'):
...: print("hard at work!")
...: cls._init_bar()
...: return cls._bar
...:
In [38]: class Foo(metaclass=MetaFoo):
...: #classmethod
...: def _init_bar(cls):
...: cls._bar = 'bar'
...:
In [39]: Foo.bar
hard at work!
Out[39]: 'bar'
In [40]: Foo.bar
Out[40]: 'bar'
Of course, while this may be possible, I'm agnostic about whether or not it is not advisable.
Edit
As #jsbueno demonstrates, it is much more sane to simply define your own descriptor, which can give you a much more flexible behavior.

defining #property.setter for a property defined in superclass [duplicate]

class human(object):
def __init__(self, name=''):
self.name = name
#property
def name(self):
return self._name
#name.setter
def name(self, value):
self._name = value
class superhuman(human):
#property
def name(self):
return 'super ' + name
s = superhuman('john')
print s.name
# Doesn't work :( "AttributeError: can't set attribute"
s.name = 'jack'
print s.name
I want to be able to override the property but be able to use the super parent's setter without having to override the setter in the child class.
Is that pythonicaly possible?
Use just the .getter decorator of the original property:
class superhuman(human):
#human.name.getter
def name(self):
return 'super ' + self._name
Note that you have to use the full name to reach the original property descriptor on the parent class.
Demonstration:
>>> class superhuman(human):
... #human.name.getter
... def name(self):
... return 'super ' + self._name
...
>>> s = superhuman('john')
>>> print s.name
super john
>>> s.name = 'jack'
>>> print s.name
super jack
The property descriptor object is just one object, even though it can have multiple methods associated with it (the getter, setter and deleter). The .getter, .setter and .deleter decorator functions provided by an existing property descriptor return a copy of the descriptor itself, with that one specific method replaced.
So in your human base class what happens is that you first create the descriptor with the #property decorator, then replace that descriptor with one that has both a getter and a setter with the #name.setter syntax. That works because python decorators replace the original decorated function with the same name, it basically executes name = name.setter(name). See How does the #property decorator work? for the details on how that all works.
In your subclass you simply use that trick to create a new copy of the descriptor with just the getter replaced.

Python: how to print instance variable of type string

I am trying to print a string variable returned by name() function, which in this case should print "Jim, but Python is printing
`<bound method Human.name of <__main__.Human object at 0x7f9a18e2aed0>>`
Below is the code.
class Human:
def __init__(self):
name = None
def setName(self, _name):
name = _name
def name(self):
return self.name
jim = Human()
jim.setName("Jim")
print(jim.name())
UPDATE:
After reading the answers, i updated the code as shown below, but, now i am getting a new error TypeError: 'str' object is not callable
class Human:
def __init__(self):
self.name = None
def setName(self, _name):
self.name = _name
def name(self):
return self.name
jim = Human()
jim.setName("Jim")
print(jim.name())
self.name is the method itself. You have no attributes storing the name. Nowhere do you actually set the name as an attribute. The following works:
class Human:
def __init__(self):
self.name = None
def setName(self, _name):
self.name = _name
# NOTE: There is no more name method here!
Now you have an actual attribute, and you don't need to call the method here:
jim = Human()
jim.setName("Jim")
print(jim.name) # directly using the attribute
You could even just set the attribute directly:
jim = Human()
jim.name = "Jim"
print(jim.name)
Alternatively, use self._name to store the name on the instance:
class Human:
_name = None
def setName(self, _name):
self._name = _name
def name(self):
return self._name
Here we used a class attribute Human._name as a default, and only set self._name on the instance in the Human.setName() method.
The problem is that name is the name of the internal variable in your object and also the name of the method.
The namespace for variables and methods is the same. Change the name of your method to something other than name. This will fix your getter. On first glance I thought that that would be all you have to do, but the recommendation in Martijn's answer also applies -- you need to assign to self.name and not just name in order to get your setter to work as well.
As an aside, this getter/setter pattern is not usually appropriate for Python. You should ask yourself why you want to use a getter/setter pattern over simply accessing the object's variable directly. See the section on getters and setters in this article for more detail.
You can use setter and getter properties instead of your custom defined methods.
class Human():
def __init__(self):
self._name = None
#property
def name(self):
return self._name
#name.setter
def name(self, name):
self._name = name
And then, use them:
jim = Human()
jim.name = "Jim"
print(jim.name)

dynamically adding callable to class as instance "method"

I implemented a metaclass that tears down the class attributes for classes created with it and builds methods from the data from those arguments, then attaches those dynamically created methods directly to the class object (the class in question allows for easy definition of web form objects for use in a web testing framework). It has been working just fine, but now I have a need to add a more complex type of method, which, to try to keep things clean, I implemented as a callable class. Unfortunately, when I try to call the callable class on an instance, it is treated as a class attribute instead of an instance method, and when called, only receives its own self. I can see why this happens, but I was hoping someone might have a better solution than the ones I've come up with. Simplified illustration of the problem:
class Foo(object):
def __init__(self, name, val):
self.name = name
self.val = val
self.__name__ = name + '_foo'
self.name = name
# This doesn't work as I'd wish
def __call__(self, instance):
return self.name + str(self.val + instance.val)
def get_methods(name, foo_val):
foo = Foo(name, foo_val)
def bar(self):
return name + str(self.val + 2)
bar.__name__ = name + '_bar'
return foo, bar
class Baz(object):
def __init__(self, val):
self.val = val
for method in get_methods('biff', 1):
setattr(Baz, method.__name__, method)
baz = Baz(10)
# baz.val == 10
# baz.biff_foo() == 'biff11'
# baz.biff_bar() == 'biff12'
I've thought of:
Using a descriptor, but that seems way more complex than is necessary here
Using a closure inside of a factory for foo, but nested closures are ugly and messy replacements for objects most of the time, imo
Wrapping the Foo instance in a method that passes its self down to the Foo instance as instance, basically a decorator, that is what I actually add to Baz, but that seems superfluous and basically just a more complicated way of doing the same thing as (2)
Is there a better way then any of these to try to accomplish what I want, or should I just bite the bullet and use some closure factory type pattern?
One way to do this is to attach the callable objects to the class as unbound methods. The method constructor will work with arbitrary callables (i.e. instances of classes with a __call__() method)—not just functions.
from types import MethodType
class Foo(object):
def __init__(self, name, val):
self.name = name
self.val = val
self.__name__ = name + '_foo'
self.name = name
def __call__(self, instance):
return self.name + str(self.val + instance.val)
class Baz(object):
def __init__(self, val):
self.val = val
Baz.biff = MethodType(Foo("biff", 42), None, Baz)
b = Baz(13)
print b.biff()
>>> biff55
In Python 3, there's no such thing as an unbound instance method (classes just have regular functions attached) so you might instead make your Foo class a descriptor that returns a bound instance method by giving it a __get__() method. (Actually, that approach will work in Python 2.x as well, but the above will perform a little better.)
from types import MethodType
class Foo(object):
def __init__(self, name, val):
self.name = name
self.val = val
self.__name__ = name + '_foo'
self.name = name
def __call__(self, instance):
return self.name + str(self.val + instance.val)
def __get__(self, instance, owner):
return MethodType(self, instance) if instance else self
# Python 2: MethodType(self, instance, owner)
class Baz(object):
def __init__(self, val):
self.val = val
Baz.biff = Foo("biff", 42)
b = Baz(13)
print b.biff()
>>> biff55
The trouble you're running into is that your object is not being bound as a method of the Baz class you're putting it in. This is because it is not a descriptor, which regular functions are!
You can fix this by adding a simple __get__ method to your Foo class that makes it into a method when it's accessed as a descriptor:
import types
class Foo(object):
# your other stuff here
def __get__(self, obj, objtype=None):
if obj is None:
return self # unbound
else:
return types.MethodType(self, obj) # bound to obj

Categories