Python new style class can not use __dict__ to update property attribute - python

>>> class foo():
... #property
... def o(self):
... return 'o'
...
>>> f = foo()
>>> f.o
'o'
>>> f.__dict__['o'] = 'f'
>>> f.o
'f'
>>> class foo(object):
... #property
... def o(self):
... return 'o'
...
>>> f = foo()
>>> f.o
'o'
>>> f.__dict__['o'] = 'f'
>>> f.o
'o'
The __dict__ set just does't appear. Any ideas why?
I think maybe Python treat property differently inside, but I am not sure about it, if you write one property class in your own, the set is gonna work, otherwise not.

#property
def o(self):
return 'o'
This is a non-data descriptor, i.e a property with no setter defined. Hence an instance attribute can override this property.
From docs:
If an instance’s dictionary has an entry with the same name as a
non-data descriptor, the dictionary entry takes precedence.
So, to update a property, define its setter method as well.
And don't use instance's __dict__ to access the setter or getter, internally f.o = 'f' for a setter is actually converted to:
type(f).__dict__['o'].__set__(f, 'f')
Demo:
class Foo:
def __init__(self):
self._o = 'o'
#property
def o(self):
return self._o
#o.setter
def o(self, val):
self._o = val
>>> f = Foo()
>>> f.o
'o'
>>> type(f).__dict__['o'].__set__(f, 'f')
>>> f.o
'f'
>>> f.o = 'zzzz' #this is equivalent to the previous call.
>>> f.o
'zzzz'
>>> type(f).__dict__['o'].__set__(f, 'foo')
>>> f.o
'foo'

Related

Confused about the property decorator and its related getter/setter methods

I've been doing some Python, and I realised I Haven't actually know a lot about the property decorator, so I tried making a simple example. This is the code I used:
class foo():
def __init__(self):
self.__test = 0
#property
def test(self):
return self.__test
#test.setter
def test(self, value):
self.__test = value
#test.getter
def test(self):
self.__test += 1
return self.__test
Then I started playing with it in the interactive shell:
>>> bar = foo()
>>> bar.test
1
>>> bar.test
2
So far the object behaved as I expected it to.
Then I tried checking out the setter method
>>> bar.test = 5
>>> bar.test
5
>>> bar.test
5
Weird. For some reason the value of __test wasn't incremented.
>>> bar._foo__test
2
I thought I had set __test to be equal to 5.
What's going on?
The problem is that your foo class is an old style class, descriptors (and as such properties) are only intended to work with new style classes.
From the doc:
Note that descriptors are only invoked for new style objects or classes (a class is new style if it inherits from object or type)
In this case, with an old style class setting bar.test = 5 creates a test attribute in the instance dict, which shadows the property from the class dict:
>>> bar = foo()
>>> foo.__dict__
{'test': <property object at 0x7f302e64c628>, '__module__': '__main__', '__doc__': None, '__init__': <function __init__ at 0x7f302e658b18>}
>>> bar.test # test property from class dict is used
1
>>> bar.__dict__
{'_foo__test': 1}
>>> bar.test = 5 # sets test on instance
>>> bar.__dict__
{'test': 5, '_foo__test': 1}
So the solution is simple: make foo a new style class by inheriting from object

Get name of the bound method from instance of the bound method object in Python

I have :
class A:
def a():
pass
After typing in the python command line:
Aobj = A()
aBoundMeth = getattr(Aobj, 'a')
My goal is to get the name of the method that aBoundMeth object represents. Is it possible to do it?
Thank you in advance.
Assuming that the name of the method is the string 'a' (in this case), You can use the __name__ attribute on the function object.
e.g.
>>> Aobj = A()
>>> aBoundMeth = getattr(Aobj, 'a')
>>> aBoundMeth.__name__
'a'
Note that this is the function name when it was created. You can make more references to the same function, but the name doesn't change. e.g.
>>> class A(object):
... def a(self):
... pass
... b = a
...
>>> Aobj = A()
>>> Aobj.a.__name__
'a'
>>> Aobj.b.__name__
'a'

How do we get an optional class attribute in Python?

For dictionary we can use .get method. What about a class's attribute and provide a default value?
You can use hasattr and getattr.
For example:
hasattr(foo, 'bar')
would return True if foo has an attribute named bar, otherwise False and
getattr(foo, 'bar', 'quux')
would return foo.bar if it exists, otherwise defaults to quux.
It's dead simple: use a keyword argument.
>>> class Foo(object):
... def __init__(self, my_foo=None):
... self.my_foo = my_foo
...
>>> f = Foo()
>>> f.my_foo
>>> repr(f.my_foo)
'None'
>>> f2 = Foo(2)
>>> f2.my_foo
2
If you are looking for an object which does not have an attribute until you set it, Jason's idea is pretty good unless you directly refer to the attribute. You'll have to work around the AttributeError you'll get. Personally, I'm not a fan of creating objects which must be constantly surrounded by try/except blocks just for the sake of not setting an instance attribute.
Python isn't much for getters and setters. However, you can use property() to work around this problem:
>>> class Foo(object):
... def __init__(self):
... pass
... def get_my_foo(self):
... return getattr(self, "_my_foo", "there's no my_foo")
... def set_my_foo(self, foo):
... self._my_foo = foo
... my_foo = property(get_my_foo, set_my_foo)
...
>>> f = Foo()
>>> f.my_foo
"there's no my_foo"
>>> f.my_foo = "foo!"
>>> f.my_foo
'foo!'
It works just as well to call get_my_foo and set_my_foo, if you like. An added benefit is that you can omit the setter to make a read-only attribute, provided someone using your object doesn't change the underlying _my_foo.

How do I assign a property to an instance in Python?

Using python, one can set an attribute of a instance via either of the two methods below:
>>> class Foo(object):
pass
>>> a = Foo()
>>> a.x = 1
>>> a.x
1
>>> setattr(a, 'b', 2)
>>> a.b
2
One can also assign properties via the property decorator.
>>> class Bar(object):
#property
def x(self):
return 0
>>> a = Bar()
>>> a.x
0
My question is, how can I assign a property to an instance?
My intuition was to try something like this...
>>> class Doo(object):
pass
>>> a = Doo()
>>> def k():
return 0
>>> a.m = property(k)
>>> a.m
<property object at 0x0380F540>
... but, I get this weird property object. Similar experimentation yielded similar results. My guess is that properties are more closely related to classes than instances in some respect, but I don't know the inner workings well enough to understand what's going on here.
It is possible to dynamically add properties to a class after it's already created:
class Bar(object):
def x(self):
return 0
setattr(Bar, 'x', property(Bar.x))
print Bar.x
# <property object at 0x04D37270>
print Bar().x
# 0
However, you can't set a property on an instance, only on a class. You can use an instance to do it:
class Bar(object):
def x(self):
return 0
bar = Bar()
setattr(bar.__class__, 'x', property(bar.__class__.x))
print Bar.x
# <property object at 0x04D306F0>
print bar.x
# 0
See How to add property to a class dynamically? for more information.
Properties use descriptors which only work on classes and thus
for all instances. But you could use a combination of a descriptor on
a class that would consult a per-instance function.
>>> class Foo(object):
... #property
... def x(self):
... if 'x' in self.__dict__:
... return self.__dict__['x'](self)
...
>>> a = Foo()
>>> def k(self):
... return 0
...
>>> a.__dict__['x'] = k
>>> a.x
0
You can assign the property directly to the class object:
>>> class Foo(object):
pass
>>> a = Foo()
>>> a.__class__
__main__.Foo
>>> setattr(a.__class__, 'm', property(lambda self: 0))
>>> a.m
0
>>> a.m = 24
AttributeError: can't set attribute
Here we have taken #agf's solution and used a lambda function to define the class property.
class A(object):
pass
a = A()
a.__class__.f = property(lambda self: 57)
a.f # 57
The following post provides more context: https://crosscompute.com/n/jAbsB6OIm6oCCJX9PBIbY5FECFKCClyV/_/Assign%20a%20class%20property%20to%20an%20instance

Getting the class name of an instance

How do I find out the name of the class used to create an instance of an object in Python?
I'm not sure if I should use the inspect module or parse the __class__ attribute.
Have you tried the __name__ attribute of the class? ie type(x).__name__ will give you the name of the class, which I think is what you want.
>>> import itertools
>>> x = itertools.count(0)
>>> type(x).__name__
'count'
If you're still using Python 2, note that the above method works with new-style classes only (in Python 3+ all classes are "new-style" classes). Your code might use some old-style classes. The following works for both:
x.__class__.__name__
Do you want the name of the class as a string?
instance.__class__.__name__
type() ?
>>> class A:
... def whoami(self):
... print(type(self).__name__)
...
>>>
>>> class B(A):
... pass
...
>>>
>>>
>>> o = B()
>>> o.whoami()
'B'
>>>
class A:
pass
a = A()
str(a.__class__)
The sample code above (when input in the interactive interpreter) will produce '__main__.A' as opposed to 'A' which is produced if the __name__ attribute is invoked. By simply passing the result of A.__class__ to the str constructor the parsing is handled for you. However, you could also use the following code if you want something more explicit.
"{0}.{1}".format(a.__class__.__module__,a.__class__.__name__)
This behavior can be preferable if you have classes with the same name defined in separate modules.
The sample code provided above was tested in Python 2.7.5.
In Python 2,
type(instance).__name__ != instance.__class__.__name__
# if class A is defined like
class A():
...
type(instance) == instance.__class__
# if class A is defined like
class A(object):
...
Example:
>>> class aclass(object):
... pass
...
>>> a = aclass()
>>> type(a)
<class '__main__.aclass'>
>>> a.__class__
<class '__main__.aclass'>
>>>
>>> type(a).__name__
'aclass'
>>>
>>> a.__class__.__name__
'aclass'
>>>
>>> class bclass():
... pass
...
>>> b = bclass()
>>>
>>> type(b)
<type 'instance'>
>>> b.__class__
<class __main__.bclass at 0xb765047c>
>>> type(b).__name__
'instance'
>>>
>>> b.__class__.__name__
'bclass'
>>>
Alternatively you can use the classmethod decorator:
class A:
#classmethod
def get_classname(cls):
return cls.__name__
def use_classname(self):
return self.get_classname()
Usage:
>>> A.get_classname()
'A'
>>> a = A()
>>> a.get_classname()
'A'
>>> a.use_classname()
'A'
Good question.
Here's a simple example based on GHZ's which might help someone:
>>> class person(object):
def init(self,name):
self.name=name
def info(self)
print "My name is {0}, I am a {1}".format(self.name,self.__class__.__name__)
>>> bob = person(name='Robert')
>>> bob.info()
My name is Robert, I am a person
Apart from grabbing the special __name__ attribute, you might find yourself in need of the qualified name for a given class/function. This is done by grabbing the types __qualname__.
In most cases, these will be exactly the same, but, when dealing with nested classes/methods these differ in the output you get. For example:
class Spam:
def meth(self):
pass
class Bar:
pass
>>> s = Spam()
>>> type(s).__name__
'Spam'
>>> type(s).__qualname__
'Spam'
>>> type(s).Bar.__name__ # type not needed here
'Bar'
>>> type(s).Bar.__qualname__ # type not needed here
'Spam.Bar'
>>> type(s).meth.__name__
'meth'
>>> type(s).meth.__qualname__
'Spam.meth'
Since introspection is what you're after, this is always you might want to consider.
You can simply use __qualname__ which stands for qualified name of a function or class
Example:
>>> class C:
... class D:
... def meth(self):
... pass
...
>>> C.__qualname__
'C'
>>> C.D.__qualname__
'C.D'
>>> C.D.meth.__qualname__
'C.D.meth'
documentation link qualname
To get instance classname:
type(instance).__name__
or
instance.__class__.__name__
both are the same
You can first use type and then str to extract class name from it.
class foo:pass;
bar:foo=foo();
print(str(type(bar))[8:-2][len(str(type(bar).__module__))+1:]);
Result
foo
If you're looking to solve this for a list (or iterable collection) of objects, here's how I would solve:
from operator import attrgetter
# Will use a few data types to show a point
my_list = [1, "2", 3.0, [4], object(), type, None]
# I specifically want to create a generator
my_class_names = list(map(attrgetter("__name__"), map(type, my_list))))
# Result:
['int', 'str', 'float', 'list', 'object', 'type', 'NoneType']
# Alternatively, use a lambda
my_class_names = list(map(lambda x: type(x).__name__, my_list))

Categories