Monkey patching a #property - python

Is it at all possible to monkey patch the value of a #property of an instance of a class that I do not control?
class Foo:
#property
def bar(self):
return here().be['dragons']
f = Foo()
print(f.bar) # baz
f.bar = 42 # MAGIC!
print(f.bar) # 42
Obviously the above would produce an error when trying to assign to f.bar. Is # MAGIC! possible in any way? The implementation details of the #property are a black box and not indirectly monkey-patchable. The entire method call needs to be replaced. It needs to affect a single instance only (class-level patching is okay if inevitable, but the changed behaviour must only selectively affect a given instance, not all instances of that class).

Subclass the base class (Foo) and change single instance's class to match the new subclass using __class__ attribute:
>>> class Foo:
... #property
... def bar(self):
... return 'Foo.bar'
...
>>> f = Foo()
>>> f.bar
'Foo.bar'
>>> class _SubFoo(Foo):
... bar = 0
...
>>> f.__class__ = _SubFoo
>>> f.bar
0
>>> f.bar = 42
>>> f.bar
42

from module import ClassToPatch
def get_foo(self):
return 'foo'
setattr(ClassToPatch, 'foo', property(get_foo))

To monkey patch a property, there is an even simpler way:
from module import ClassToPatch
def get_foo(self):
return 'foo'
ClassToPatch.foo = property(get_foo)

Idea: replace property descriptor to allow setting on certain objects. Unless a value is explicitly set this way, original property getter is called.
The problem is how to store the explicitly set values. We cannot use a dict keyed by patched objects, since 1) they are not necessarily comparable by identity; 2) this prevents patched objects from being garbage-collected. For 1) we could write a Handle that wraps objects and overrides comparison semantics by identity and for 2) we could use weakref.WeakKeyDictionary. However, I couldn't make these two work together.
Therefore we use a different approach of storing the explicitly set values on the object itself, using a "very unlikely attribute name". It is of course still possible that this name would collide with something, but that's pretty much inherent to languages such as Python.
This won't work on objects that lack a __dict__ slot. Similar problem would arise for weakrefs though.
class Foo:
#property
def bar (self):
return 'original'
class Handle:
def __init__(self, obj):
self._obj = obj
def __eq__(self, other):
return self._obj is other._obj
def __hash__(self):
return id (self._obj)
_monkey_patch_index = 0
_not_set = object ()
def monkey_patch (prop):
global _monkey_patch_index, _not_set
special_attr = '$_prop_monkey_patch_{}'.format (_monkey_patch_index)
_monkey_patch_index += 1
def getter (self):
value = getattr (self, special_attr, _not_set)
return prop.fget (self) if value is _not_set else value
def setter (self, value):
setattr (self, special_attr, value)
return property (getter, setter)
Foo.bar = monkey_patch (Foo.bar)
f = Foo()
print (Foo.bar.fset)
print(f.bar) # baz
f.bar = 42 # MAGIC!
print(f.bar) # 42

It looks like you need to move on from properties to the realms of data descriptors and non-data descriptors. Properties are just a specialised version of data descriptors. Functions are an example of non-data descriptors -- when you retrieve them from an instance they return a method rather than the function itself.
A non-data descriptor is just an instance of a class that has a __get__ method. The only difference with a data descriptor is that it has a __set__ method as well. Properties initially have a __set__ method that throws an error unless you provide a setter function.
You can achieve what you want really easily just by writing your own trivial non-data descriptor.
class nondatadescriptor:
"""generic nondata descriptor decorator to replace #property with"""
def __init__(self, func):
self.func = func
def __get__(self, obj, objclass):
if obj is not None:
# instance based access
return self.func(obj)
else:
# class based access
return self
class Foo:
#nondatadescriptor
def bar(self):
return "baz"
foo = Foo()
another_foo = Foo()
assert foo.bar == "baz"
foo.bar = 42
assert foo.bar == 42
assert another_foo.bar == "baz"
del foo.bar
assert foo.bar == "baz"
print(Foo.bar)
What makes all this work is that logic under the hood __getattribute__. I can't find the appropriate documentation at the moment, but order of retrieval is:
Data descriptors defined on the class are given the highest priority (objects with both __get__ and __set__), and their __get__ method is invoked.
Any attribute of the object itself.
Non-data descriptors defined on the class (objects with only a __get__ method).
All other attributes defined on the class.
Finally the __getattr__ method of the object is invoked as a last resort (if defined).

You can also patch property setters. Using #fralau 's answer:
from module import ClassToPatch
def foo(self, new_foo):
self._foo = new_foo
ClassToPatch.foo = ClassToPatch.foo.setter(foo)
reference

In case someone needs to patch a property while being able to call the original implementation, here is an example:
#property
def _cursor_args(self, __orig=mongoengine.queryset.base.BaseQuerySet._cursor_args):
# TODO: remove this hack when we upgrade MongoEngine
# https://github.com/MongoEngine/mongoengine/pull/2160
cursor_args = __orig.__get__(self)
if self._timeout:
cursor_args.pop("no_cursor_timeout", None)
return cursor_args
mongoengine.queryset.base.BaseQuerySet._cursor_args = _cursor_args

Related

Python: Check if a method uses #staticmethod [duplicate]

assume following class definition:
class A:
def f(self):
return 'this is f'
#staticmethod
def g():
return 'this is g'
a = A()
So f is a normal method and g is a static method.
Now, how can I check if the funcion objects a.f and a.g are static or not? Is there a "isstatic" funcion in Python?
I have to know this because I have lists containing many different function (method) objects, and to call them I have to know if they are expecting "self" as a parameter or not.
Lets experiment a bit:
>>> import types
>>> class A:
... def f(self):
... return 'this is f'
... #staticmethod
... def g():
... return 'this is g'
...
>>> a = A()
>>> a.f
<bound method A.f of <__main__.A instance at 0x800f21320>>
>>> a.g
<function g at 0x800eb28c0>
>>> isinstance(a.g, types.FunctionType)
True
>>> isinstance(a.f, types.FunctionType)
False
So it looks like you can use types.FunctionType to distinguish static methods.
Your approach seems a bit flawed to me, but you can check class attributes:
(in Python 2.7):
>>> type(A.f)
<type 'instancemethod'>
>>> type(A.g)
<type 'function'>
or instance attributes in Python 3.x
>>> a = A()
>>> type(a.f)
<type 'method'>
>>> type(a.g)
<type 'function'>
To supplement the answers here, in Python 3 the best way is like so:
import inspect
class Test:
#staticmethod
def test(): pass
isstatic = isinstance(inspect.getattr_static(Test, "test"), staticmethod)
We use getattr_static rather than getattr, since getattr will retrieve the bound method or function, not the staticmethod class object. You can do a similar check for classmethod types and property's (e.g. attributes defined using the #property decorator)
Note that even though it is a staticmethod, don't assume it was defined inside the class. The method source may have originated from another class. To get the true source, you can look at the underlying function's qualified name and module. For example:
class A:
#staticmethod:
def test(): pass
class B: pass
B.test = inspect.getattr_static(A, "test")
print("true source: ", B.test.__qualname__)
Technically, any method can be used as "static" methods, so long as they are called on the class itself, so just keep that in mind. For example, this will work perfectly fine:
class Test:
def test():
print("works!")
Test.test()
That example will not work with instances of Test, since the method will be bound to the instance and called as Test.test(self) instead.
Instance and class methods can be used as static methods as well in some cases, so long as the first arg is handled properly.
class Test:
def test(self):
print("works!")
Test.test(None)
Perhaps another rare case is a staticmethod that is also bound to a class or instance. For example:
class Test:
#classmethod
def test(cls): pass
Test.static_test = staticmethod(Test.test)
Though technically it is a staticmethod, it is really behaving like a classmethod. So in your introspection, you may consider checking the __self__ (recursively on __func__) to see if the method is bound to a class or instance.
I happens to have a module to solve this. And it's Python2/3 compatible solution. And it allows to test with method inherit from parent class.
Plus, this module can also test:
regular attribute
property style method
regular method
staticmethod
classmethod
For example:
class Base(object):
attribute = "attribute"
#property
def property_method(self):
return "property_method"
def regular_method(self):
return "regular_method"
#staticmethod
def static_method():
return "static_method"
#classmethod
def class_method(cls):
return "class_method"
class MyClass(Base):
pass
Here's the solution for staticmethod only. But I recommend to use the module posted here.
import inspect
def is_static_method(klass, attr, value=None):
"""Test if a value of a class is static method.
example::
class MyClass(object):
#staticmethod
def method():
...
:param klass: the class
:param attr: attribute name
:param value: attribute value
"""
if value is None:
value = getattr(klass, attr)
assert getattr(klass, attr) == value
for cls in inspect.getmro(klass):
if inspect.isroutine(value):
if attr in cls.__dict__:
bound_value = cls.__dict__[attr]
if isinstance(bound_value, staticmethod):
return True
return False
Why bother? You can just call g like you call f:
a = A()
a.f()
a.g()

Python: Class private attributes [duplicate]

I am generally confused about the difference between a "property" and an "attribute", and can't find a great resource to concisely detail the differences.
Properties are a special kind of attribute. Basically, when Python encounters the following code:
spam = SomeObject()
print(spam.eggs)
it looks up eggs in spam, and then examines eggs to see if it has a __get__, __set__, or __delete__ method — if it does, it's a property. If it is a property, instead of just returning the eggs object (as it would for any other attribute) it will call the __get__ method (since we were doing lookup) and return whatever that method returns.
More information about Python's data model and descriptors.
With a property you have complete control on its getter, setter and deleter methods, which you don't have (if not using caveats) with an attribute.
class A(object):
_x = 0
'''A._x is an attribute'''
#property
def x(self):
'''
A.x is a property
This is the getter method
'''
return self._x
#x.setter
def x(self, value):
"""
This is the setter method
where I can check it's not assigned a value < 0
"""
if value < 0:
raise ValueError("Must be >= 0")
self._x = value
>>> a = A()
>>> a._x = -1
>>> a.x = -1
Traceback (most recent call last):
File "ex.py", line 15, in <module>
a.x = -1
File "ex.py", line 9, in x
raise ValueError("Must be >= 0")
ValueError: Must be >= 0
In general speaking terms a property and an attribute are the same thing. However, there is a property decorator in Python which provides getter/setter access to an attribute (or other data).
class MyObject(object):
# This is a normal attribute
foo = 1
#property
def bar(self):
return self.foo
#bar.setter
def bar(self, value):
self.foo = value
obj = MyObject()
assert obj.foo == 1
assert obj.bar == obj.foo
obj.bar = 2
assert obj.foo == 2
assert obj.bar == obj.foo
The property allows you to get and set values like you would normal attributes, but underneath there is a method being called translating it into a getter and setter for you. It's really just a convenience to cut down on the boilerplate of calling getters and setters.
Lets say for example, you had a class that held some x and y coordinates for something you needed. To set them you might want to do something like:
myObj.x = 5
myObj.y = 10
That is much easier to look at and think about than writing:
myObj.setX(5)
myObj.setY(10)
The problem is, what if one day your class changes such that you need to offset your x and y by some value? Now you would need to go in and change your class definition and all of the code that calls it, which could be really time consuming and error prone. The property allows you to use the former syntax while giving you the flexibility of change of the latter.
In Python, you can define getters, setters, and delete methods with the property function. If you just want the read property, there is also a #property decorator you can add above your method.
http://docs.python.org/library/functions.html#property
I learnt 2 differences from site of Bernd Klein, in summary:
1. A property is a more convenient way to achieve data encapsulation
For example, let's say you have a public attribute length. Later on, your project requires you to encapsulate it, i.e. to change it to private and provide a getter and setter => you have to change the the code you wrote before:
# Old code
obj1.length = obj1.length + obj2.length
# New code (using private attributes and getter and setter)
obj1.set_length(obj1.get_length() + obj2.get_length()) # => this is ugly
If you use #property and #length.setter => you don't need to change that old code.
2. A property can encapsulate multiple attributes
class Person:
def __init__(self, name, physic_health, mental_health):
self.name = name
self.__physic_health = physic_health
self.__mental_health = mental_health
#property
def condition(self):
health = self.__physic_health + self.__mental_health
if(health < 5.0):
return "I feel bad!"
elif health < 8.0:
return "I am ok!"
else:
return "Great!"
In this example, __physic_health and __mental_health are private and cannot be accessed directly from outside.
There is also one not obvious difference that i use to cache or refresh data , often we have a function connected to class attribute. For instance i need to read file once and keep content assigned to the attribute so the value is cached:
class Misc():
def __init__(self):
self.test = self.test_func()
def test_func(self):
print 'func running'
return 'func value'
cl = Misc()
print cl.test
print cl.test
Output:
func running
func value
func value
We accessed the attribute twice but our function was fired only once. Changing the above example to use property will cause attribute's value refresh each time you access it:
class Misc():
#property
def test(self):
print 'func running'
return 'func value'
cl = Misc()
print cl.test
print cl.test
Output:
func running
func value
func running
func value
I like to think that, if you want to set a restriction for an attribute, use a property.
Although all attributes are public, generally programmers differentiate public and private attributes with an underscore(_). Consider the following class,
class A:
def __init__(self):
self.b = 3 # To show public
self._c = 4 # To show private
Here, b attribute is intended to be accessed from outside class A. But, readers of this class might wonder, can b attribute be set from outside class A?
If we intend to not set b from outside, we can show this intention with #property.
class A:
def __init__(self):
self._c = 4 # To show private
#property
def b(self):
return 3
Now, b can't be set.
a = A()
print(a.b) # prints 3
a.b = 7 # Raises AttributeError
Or, if you wish to set only certain values,
class A:
#property
def b(self):
return self._b
#b.setter
def b(self, val):
if val < 0:
raise ValueError("b can't be negative")
self._b = val
a = A()
a.b = 6 # OK
a.b = -5 # Raises ValueError

python: super()-like proxy object that starts the MRO search at a specified class

According to the docs, super(cls, obj) returns
a proxy object that delegates method calls to a parent or sibling
class of type cls
I understand why super() offers this functionality, but I need something slightly different: I need to create a proxy object that delegates methods calls (and attribute lookups) to class cls itself; and as in super, if cls doesn't implement the method/attribute, my proxy should continue looking in the MRO order (of the new not the original class). Is there any function I can write that achieves that?
Example:
class X:
def act():
#...
class Y:
def act():
#...
class A(X, Y):
def act():
#...
class B(X, Y):
def act():
#...
class C(A, B):
def act():
#...
c = C()
b = some_magic_function(B, c)
# `b` needs to delegate calls to `act` to B, and look up attribute `s` in B
# I will pass `b` somewhere else, and have no control over it
Of course, I could do b = super(A, c), but that relies on knowing the exact class hierarchy and the fact that B follows A in the MRO. It would silently break if any of these two assumptions change in the future. (Note that super doesn't make any such assumptions!)
If I just needed to call b.act(), I could use B.act(c). But I am passing b to someone else, and have no idea what they'll do with it. I need to make sure it doesn't betray me and start acting like an instance of class C at some point.
A separate question, the documentation for super() (in Python 3.2) only talks about its method delegation, and does not clarify that attribute lookups for the proxy are also performed the same way. Is it an accidental omission?
EDIT
The updated Delegate approach works in the following example as well:
class A:
def f(self):
print('A.f')
def h(self):
print('A.h')
self.f()
class B(A):
def g(self):
self.f()
print('B.g')
def f(self):
print('B.f')
def t(self):
super().h()
a_true = A()
# instance of A ends up executing A.f
a_true.h()
b = B()
a_proxy = Delegate(A, b)
# *unlike* super(), the updated `Delegate` implementation would call A.f, not B.f
a_proxy.h()
Note that the updated class Delegate is closer to what I want than super() for two reasons:
super() only does it proxying for the first call; subsequent calls will happen as normal, since by then the object is used, not its proxy.
super() does not allow attribute access.
Thus, my question as asked has a (nearly) perfect answer in Python.
It turns out that, at a higher level, I was trying to do something I shouldn't (see my comments here).
This class should cover the most common cases:
class Delegate:
def __init__(self, cls, obj):
self._delegate_cls = cls
self._delegate_obj = obj
def __getattr__(self, name):
x = getattr(self._delegate_cls, name)
if hasattr(x, "__get__"):
return x.__get__(self._delegate_obj)
return x
Use it like this:
b = Delegate(B, c)
(with the names from your example code.)
Restrictions:
You cannot retrieve some special attributes like __class__ etc. from the class you pass in the constructor via this proxy. (This restistions also applies to super.)
This might behave weired if the attribute you want to retrieve is some weired kind of descriptor.
Edit: If you want the code in the update to your question to work as desired, you can use the foloowing code:
class Delegate:
def __init__(self, cls):
self._delegate_cls = cls
def __getattr__(self, name):
x = getattr(self._delegate_cls, name)
if hasattr(x, "__get__"):
return x.__get__(self)
return x
This passes the proxy object as self parameter to any called method, and it doesn't need the original object at all, hence I deleted it from the constructor.
If you also want instance attributes to be accessible you can use this version:
class Delegate:
def __init__(self, cls, obj):
self._delegate_cls = cls
self._delegate_obj = obj
def __getattr__(self, name):
if name in vars(self._delegate_obj):
return getattr(self._delegate_obj, name)
x = getattr(self._delegate_cls, name)
if hasattr(x, "__get__"):
return x.__get__(self)
return x
A separate question, the documentation for super() (in Python 3.2)
only talks about its method delegation, and does not clarify that
attribute lookups for the proxy are also performed the same way. Is it
an accidental omission?
No, this is not accidental. super() does nothing for attribute lookups. The reason is that attributes on an instance are not associated with a particular class, they're just there. Consider the following:
class A:
def __init__(self):
self.foo = 'foo set from A'
class B(A):
def __init__(self):
super().__init__()
self.bar = 'bar set from B'
class C(B):
def method(self):
self.baz = 'baz set from C'
class D(C):
def __init__(self):
super().__init__()
self.foo = 'foo set from D'
self.baz = 'baz set from D'
instance = D()
instance.method()
instance.bar = 'not set from a class at all'
Which class "owns" foo, bar, and baz?
If I wanted to view instance as an instance of C, should it have a baz attribute before method is called? How about afterwards?
If I view instance as an instance of A, what value should foo have? Should bar be invisible because was only added in B, or visible because it was set to a value outside the class?
All of these questions are nonsense in Python. There's no possible way to design a system with the semantics of Python that could give sensible answers to them. __init__ isn't even special in terms of adding attributes to instances of the class; it's just a perfectly ordinary method that happens to be called as part of the instance creation protocol. Any method (or indeed code from another class altogether, or not from any class at all) can create attributes on any instance it has a reference to.
In fact, all of the attributes of instance are stored in the same place:
>>> instance.__dict__
{'baz': 'baz set from C', 'foo': 'foo set from D', 'bar': 'not set from a class at all'}
There's no way to tell which of them were originally set by which class, or were last set by which class, or whatever measure of ownership you want. There's certainly no way to get at "the A.foo being shadowed by D.foo", as you would expect from C++; they're the same attribute, and any writes to to it by one class (or from elsewhere) will clobber a value left in it by the other class.
The consequence of this is that super() does not perform attribute lookups the same way it does method lookups; it can't, and neither can any code you write.
In fact, from running some experiments, neither super nor Sven's Delegate actually support direct attribute retrieval at all!
class A:
def __init__(self):
self.spoon = 1
self.fork = 2
def foo(self):
print('A.foo')
class B(A):
def foo(self):
print('B.foo')
b = B()
d = Delegate(A, b)
s = super(B, b)
Then both work as expected for methods:
>>> d.foo()
A.foo
>>> s.foo()
A.foo
But:
>>> d.fork
Traceback (most recent call last):
File "<pyshell#43>", line 1, in <module>
d.fork
File "/tmp/foo.py", line 6, in __getattr__
x = getattr(self._delegate_cls, name)
AttributeError: type object 'A' has no attribute 'fork'
>>> s.spoon
Traceback (most recent call last):
File "<pyshell#45>", line 1, in <module>
s.spoon
AttributeError: 'super' object has no attribute 'spoon'
So they both only really work for calling some methods on, not for passing to arbitrary third party code to pretend to be an instance of the class you want to delegate to.
They don't behave the same way in the presence of multiple inheritance unfortunately. Given:
class Delegate:
def __init__(self, cls, obj):
self._delegate_cls = cls
self._delegate_obj = obj
def __getattr__(self, name):
x = getattr(self._delegate_cls, name)
if hasattr(x, "__get__"):
return x.__get__(self._delegate_obj)
return x
class A:
def foo(self):
print('A.foo')
class B:
pass
class C(B, A):
def foo(self):
print('C.foo')
c = C()
d = Delegate(B, c)
s = super(C, c)
Then:
>>> d.foo()
Traceback (most recent call last):
File "<pyshell#50>", line 1, in <module>
d.foo()
File "/tmp/foo.py", line 6, in __getattr__
x = getattr(self._delegate_cls, name)
AttributeError: type object 'B' has no attribute 'foo'
>>> s.foo()
A.foo
Because Delegate ignores the full MRO of whatever class _delegate_obj is an instance of, only using the MRO of _delegate_cls. Whereas super does what you asked in the question, but the behaviour seems quite strange: it's not wrapping an instance of C to pretend it's an instance of B, because direct instances of B don't have foo defined.
Here's my attempt:
class MROSkipper:
def __init__(self, cls, obj):
self.__cls = cls
self.__obj = obj
def __getattr__(self, name):
mro = self.__obj.__class__.__mro__
i = mro.index(self.__cls)
if i == 0:
# It's at the front anyway, just behave as getattr
return getattr(self.__obj, name)
else:
# Check __dict__ not getattr, otherwise we'd find methods
# on classes we're trying to skip
try:
return self.__obj.__dict__[name]
except KeyError:
return getattr(super(mro[i - 1], self.__obj), name)
I rely on the __mro__ attribute of classes to properly figure out where to start from, then I just use super. You could walk the MRO chain from that point yourself checking class __dict__s for methods instead if the weirdness of going back one step to use super is too much.
I've made no attempt to handle unusual attributes; those implemented with descriptors (including properties), or those magic methods looked up behind the scenes by Python, which often start at the class rather than the instance directly. But this behaves as you asked moderately well (with the caveat expounded on ad nauseum in the first part of my post; looking up attributes this way will not give you any different results than looking them up directly in the instance).

What's the difference between a Python "property" and "attribute"?

I am generally confused about the difference between a "property" and an "attribute", and can't find a great resource to concisely detail the differences.
Properties are a special kind of attribute. Basically, when Python encounters the following code:
spam = SomeObject()
print(spam.eggs)
it looks up eggs in spam, and then examines eggs to see if it has a __get__, __set__, or __delete__ method — if it does, it's a property. If it is a property, instead of just returning the eggs object (as it would for any other attribute) it will call the __get__ method (since we were doing lookup) and return whatever that method returns.
More information about Python's data model and descriptors.
With a property you have complete control on its getter, setter and deleter methods, which you don't have (if not using caveats) with an attribute.
class A(object):
_x = 0
'''A._x is an attribute'''
#property
def x(self):
'''
A.x is a property
This is the getter method
'''
return self._x
#x.setter
def x(self, value):
"""
This is the setter method
where I can check it's not assigned a value < 0
"""
if value < 0:
raise ValueError("Must be >= 0")
self._x = value
>>> a = A()
>>> a._x = -1
>>> a.x = -1
Traceback (most recent call last):
File "ex.py", line 15, in <module>
a.x = -1
File "ex.py", line 9, in x
raise ValueError("Must be >= 0")
ValueError: Must be >= 0
In general speaking terms a property and an attribute are the same thing. However, there is a property decorator in Python which provides getter/setter access to an attribute (or other data).
class MyObject(object):
# This is a normal attribute
foo = 1
#property
def bar(self):
return self.foo
#bar.setter
def bar(self, value):
self.foo = value
obj = MyObject()
assert obj.foo == 1
assert obj.bar == obj.foo
obj.bar = 2
assert obj.foo == 2
assert obj.bar == obj.foo
The property allows you to get and set values like you would normal attributes, but underneath there is a method being called translating it into a getter and setter for you. It's really just a convenience to cut down on the boilerplate of calling getters and setters.
Lets say for example, you had a class that held some x and y coordinates for something you needed. To set them you might want to do something like:
myObj.x = 5
myObj.y = 10
That is much easier to look at and think about than writing:
myObj.setX(5)
myObj.setY(10)
The problem is, what if one day your class changes such that you need to offset your x and y by some value? Now you would need to go in and change your class definition and all of the code that calls it, which could be really time consuming and error prone. The property allows you to use the former syntax while giving you the flexibility of change of the latter.
In Python, you can define getters, setters, and delete methods with the property function. If you just want the read property, there is also a #property decorator you can add above your method.
http://docs.python.org/library/functions.html#property
I learnt 2 differences from site of Bernd Klein, in summary:
1. A property is a more convenient way to achieve data encapsulation
For example, let's say you have a public attribute length. Later on, your project requires you to encapsulate it, i.e. to change it to private and provide a getter and setter => you have to change the the code you wrote before:
# Old code
obj1.length = obj1.length + obj2.length
# New code (using private attributes and getter and setter)
obj1.set_length(obj1.get_length() + obj2.get_length()) # => this is ugly
If you use #property and #length.setter => you don't need to change that old code.
2. A property can encapsulate multiple attributes
class Person:
def __init__(self, name, physic_health, mental_health):
self.name = name
self.__physic_health = physic_health
self.__mental_health = mental_health
#property
def condition(self):
health = self.__physic_health + self.__mental_health
if(health < 5.0):
return "I feel bad!"
elif health < 8.0:
return "I am ok!"
else:
return "Great!"
In this example, __physic_health and __mental_health are private and cannot be accessed directly from outside.
There is also one not obvious difference that i use to cache or refresh data , often we have a function connected to class attribute. For instance i need to read file once and keep content assigned to the attribute so the value is cached:
class Misc():
def __init__(self):
self.test = self.test_func()
def test_func(self):
print 'func running'
return 'func value'
cl = Misc()
print cl.test
print cl.test
Output:
func running
func value
func value
We accessed the attribute twice but our function was fired only once. Changing the above example to use property will cause attribute's value refresh each time you access it:
class Misc():
#property
def test(self):
print 'func running'
return 'func value'
cl = Misc()
print cl.test
print cl.test
Output:
func running
func value
func running
func value
I like to think that, if you want to set a restriction for an attribute, use a property.
Although all attributes are public, generally programmers differentiate public and private attributes with an underscore(_). Consider the following class,
class A:
def __init__(self):
self.b = 3 # To show public
self._c = 4 # To show private
Here, b attribute is intended to be accessed from outside class A. But, readers of this class might wonder, can b attribute be set from outside class A?
If we intend to not set b from outside, we can show this intention with #property.
class A:
def __init__(self):
self._c = 4 # To show private
#property
def b(self):
return 3
Now, b can't be set.
a = A()
print(a.b) # prints 3
a.b = 7 # Raises AttributeError
Or, if you wish to set only certain values,
class A:
#property
def b(self):
return self._b
#b.setter
def b(self, val):
if val < 0:
raise ValueError("b can't be negative")
self._b = val
a = A()
a.b = 6 # OK
a.b = -5 # Raises ValueError

foo and _foo - about variables inside a class

class ClassName(object):
"""
"""
def __init__(self, foo, bar):
"""
"""
self.foo = foo # read-write property
self.bar = bar # simple attribute
def _set_foo(self, value):
self._foo = value
def _get_foo(self):
return self._foo
foo = property(_get_foo, _set_foo)
a = ClassName(1,2)
#a._set_foo(3)
print a._get_foo()
When I print a._get_foo() the function _get_foo prints the variable self._foo .
But where does it come from?
self._foo and self.foo are different, aren't they?
EDIT: The problem is that I still not understand what property does. Give me some time.
Docs for property explain how it's supposed to be used. There is no difference between these lines:
self.foo = foo # read-write property
and
a.foo = 3
You're also not supposed to call setters and getters manually.
eta: if you don't understand what property does after looking at the examples provided in the docs and reading our answers, you perhaps should just abstain from using it. It really is not the most essential of Python's features. You might want to start with a simpler example, which is easy to understand:
>>> class ClassName(object):
def __init__(self, foo):
self.foo = foo
>>> a = ClassName(1, 2)
>>> a.foo
1
>>> a.foo = 42
>>> a.foo
42
I really recommend this site:
http://adam.gomaa.us/blog/2008/aug/11/the-python-property-builtin/
A good explanation of the property-function.
You define foo to be a property with getters and setters.
Those getters and setters use the variable self._foo, since you coded it that way:
def _set_foo(self, value):
self._foo = value
In your case you would get a problem if you had a slightly different implementation:
class ClassName(object):
"""
"""
def __init__(self,):
"""
"""
pass
def _set_foo(self, value):
self._foo = value
def _get_foo(self):
return self._foo
foo = property(_get_foo, _set_foo)
a = ClassName()
print a.foo
-> AttributeError
b = ClassName()
b.foo = 1 # implicitely sets self._foo !
print b.foo
>> OK, this is 1
As it happens you indeed should set self.foo in the __init__ since the setter creates self._foo. (Or initialize self._foo in the __init__ directly of course).
So, where do self.foo and self._foo come from? It's explicit in the code. self.foo is a property, and self._foo is the variable in which you decided to keep the value of that property.
self._foo and self.foo would be different except that you have assigned the property you created to the name foo: foo = property(_get_foo, _set_foo).
So now you have two names defined foo which is property that is defined in terms of the methods _set_foo and _get_foo. You also have a name _foo defined but this is the same attribute used in _set_foo and _get_foo and therefore in property foo.
So you two names but only one object.
foo
/\
/ \
_set_foo _get_foo
\ /
\ /
_foo --> object
Note: For the example you have Python this is overkill, there is no need to use getter and setter methods unless they are going to have business logic in them. Just use an attribute named foo, you can always wrap that in a property in future without affecting client code if it turns out that you need some smarts around the access of the attribute.
Change your code to:
class ClassName(object):
def __init__(self, foo, bar):
self.foo = foo # read-write property
self.bar = bar # simple attribute
Both foo and bar are read write, if you need finer control you can then consider using a property.
According to your code, it "comes from" _set_foo. In your init, when you do self.foo = foo, that calls _set_foo(1), which performs self._foo = 1.
You can see this more clearly if you add a print statement inside _set_foo().

Categories