Python object's attribute - python

I have a python question about object's attribute. Code:
>>> class A(object):
... dict = {}
... def stuff(self, name):
... self.dict[name] = 'toto'
...
>>> a = A()
>>> print a.dict
{}
>>> a.stuff('un')
>>> print a.dict
{'un': 'toto'}
>>> b = A()
>>> print b.dict
{'un': 'toto'}
I'm a PHP devlopper and in PHP rint b.dict will be {}. Why python share this attribute between a and b ? What is the way to define class attribute who will be new on new instantiation?

You created a class attribute, not an instance attribute. The dictionary is mutable, you can alter it from instances or on the class directly, but class attributes are by definition shared among all instances.
Create a new empty dictionary in the __init__ method instead:
class A(object):
def __init__(self):
self.dict = {}
def stuff(self, name):
self.dict[name] = 'toto'

Related

Re-defining already assigned python class

I am new to Python and I inherited someone's code that had the following code structure. Why do I get an object not callable and how can I redefine this method again even after re-assigning l.bar. Another question would therefore be what's the difference between l.bar and l.bar()?
>>> class foo(object):
... def __init__(self):
... self.name = "Food"
... class bar(object):
... def __init__(self):
... self.name = "Is"
... class tea(object):
... def __init__(self):
... self.name = "Good"
...
>>> l = foo()
>>> m = l.bar()
>>> m.name = "Was"
>>> l.bar = m
>>> r = l.bar()
Traceback (most recent call last):
File "<input>", line 1, in <module>
TypeError: 'bar' object is not callable
As others have pointed out, it's generally not good practice to have nested classes. But, here's a breakdown of what's happening:
class foo(object):
def __init__(self):
self.name = "Food"
class bar(object):
def __init__(self):
self.name = "Is"
class tea(object):
def __init__(self):
self.name = "Good"
l = foo() # l is now an instance of foo
print l.name # "Food"
m = l.bar() # m is now an instance of bar
print m.name # "Is"
m.name = "Was" # you've assigned m's name to "Was"
print m.name # "Was"
l.bar = m # you are overriding foo's nested bar class now with an instance of bar
print l.name # "Food"
print l.bar # <__main__.bar object at 0x108371ad0>: this is now an instance, not a class
print l.bar.name # "Was"
r = l.bar() # you are now trying to call an instance of bar
The last line doesn't work because of the same reasons calling l() or foo()() doesn't work.
If you absolutely must figure out a way to make foo.bar().name return something else, you can create a new class and reassign foo.bar to it. But, this is really gross and not recommended. Hopefully, you can just change that original code.
print foo.bar().name # "Is"
class NewBar(object):
def __init__(self):
self.name = 'Was'
foo.bar = NewBar
print foo.bar().name # "Was"
Why do i get an object not callable
You assigned l.bar to be an instance of the class foo.bar (specifically, you assigned m to it). Instances of that class aren't callable, therefore l.bar isn't callable.
how can i redefine this method again even after re-assigning l.bar
Maybe this advice is too obvious, but don't re-assign l.bar.
However, you can reset l.bar so that it refers to the method it originally referred to, by doing del l.bar.
The reason this works is because if the individual object has no bar attribute of its own, then Python looks next to see whether its class has an attribute of the same name. So, to begin with the expression l.bar evaluates to the class foo.bar, since l has type foo. Then you assigned l a bar attribute of its own, so l.bar suddenly starts evaluating to that object instead. You can restore normality by deleting the object's own attribute.
what's the difference between l.bar and l.bar()
l.bar just gets the value of the attribute bar from the object l (or from its class, if the object l doesn't have one of its own, as explained above. If that fails too it'd go to base classes). l.bar() gets the value of that attribute and then calls it. () at this position means a function call, so the thing you put it after had better be callable.
It is not clear which of the following problems you are experiencing:
1. indentation issue
When copy-pasting from source to terminal, indentation sometimes gets messed up. in ipython you can use %paste to safely paste code.
The correctly indented class declarations are:
class foo(object):
def __init__(self):
self.name = "Food"
class bar(object):
def __init__(self):
self.name = "Is"
class tea(object):
def __init__(self):
self.name = "Good"
But then the other commands make no sense.
2. instance is not the same as class
When defining a class inside a class, you have to use the outer class name to "get" to the inner class name. I.e.:
class foo(object):
def __init__(self):
self.name = "Food"
class bar(object):
def __init__(self):
self.name = "Is"
class tea(object):
def __init__(self):
self.name = "Good"
foo_inst = foo()
bar_inst = foo.bar()
tea_inst = foo.bar.tea()
Anyhow, these lines still make not much sense:
>>> l.bar = m
>>> r = l.bar()
Why would you want to override bar which is (was) a class name...

Automatically add newly created objects to list

Say I have a class called A and i want to list all the objects created from that particular class. This is what i have done till now and It raises AttributeError: type object 'A' has no attribute 'items' How to do this?
class A:
def __init__(self):
self.items = []
self.items.append(self)
#classmethod
def list_objects(cls):
return cls.items
a = A()
b = A()
print(A.list_objects())
# Expected output is [a,b]
You would need the list to be at the class level, not instance level
class A:
items = []
def __init__(self):
A.items.append(self)
#classmethod
def list_objects(cls):
return cls.items
Then you would see
>>> a = A()
>>> b = A()
>>> A.list_objects()
[<__main__.A object at 0x02B77230>, <__main__.A object at 0x02B772D0>]
The problem with your code is in the self.items = [] part, as you initialise a new items empty list for each instance of class A you create. So in your case each object of class A will have an instance member items, containing itself only.
So first of all you need to move your items list to the class level, and then in the __init__ add self to that list.
If you are going to need this functionality for many classes, I would suggest to go with the following solution:
#track_objects
class A:
def __init__(self):
pass # your init code here
>>> a = A()
>>> b = A()
>>> A.items
[<__main__.A instance at 0x1004873f8>, <__main__.A instance at 0x100487488>]
and this is the #track_objects implementation:
def track_objects(klass):
klass.items = []
orig_init = klass.__init__
def init_wrapper(self, *args, **kwargs):
self.items.append(self)
return orig_init(self, *args, **kwargs)
klass.__init__ = init_wrapper
return klass

Python: Copy properties with it's functions (fget, fset, fdel) from one class to another

I know the questions about: copy properties, or dynamic creation of properties has already been posted and also been answered (here, here and here). You could also find an excellent description, how the property function works here.
But I think, that my question is a bit more specific. I do not only want to copy the property from one class to another. No, I also want the specific getter, setter and deleter functions to be copied to the destination class. After a whole day of searching for an answer, I decided to create an new post for this question.
So let me get a bit more in detail. A have an attribute class which is more a class group and stores property-classes:
class AttrContainer():
class a():
ATTR=1
#property
def a(self):
return self.ATTR
#a.setter
def a(self, n):
self.ATTR = n + 3.021
class b():
ATTR=None
#property
def b(self):
return "Something"
class c():
ATTR=None
#property
def c(self):
return 3
#c.setter
def c(self, n):
self.ATTR = n - 8.5201
As you can see, I have different getter, setter (not in the example: deleter) definitions of each property.
I want to use those properties with my item "wrapper" objects. But not all of item objects needs all properties, thats why I want to copy them dynamically into my wrapper classes.
So, this is how my item "wrapper" classes looks like:
class Item01Object():
properties = ["a","c"]
ATTR = None
#[...]
class Item02Object():
properties = ["b","c"]
ATTR = None
#[...]
#[...]
Because I can't set the properties dynamically while the item class will be instanced, I have to set them before I instance the class:
def SetProperties( ItemObject ):
for propName, cls in AttrContainer.__dict__.iteritems():
if propName in ItemObject.properties:
prop = cls.__dict__[propName]
fget = prop.fget if prop.fget else None
fset = prop.fset if prop.fset else None
fdel = prop.fdel if prop.fdel else None
ItemObject.__dict__[propName] = property(fget,fset,fdel)
return ItemObject()
In the end, i would instance my ItemObjects like this:
item = SetProperties(Item01Object)
I would expect, that this will work...
>>> print item
<__builtin__.Item01Object instance at 0x0000000003270F88>
>>> print item.a
None
This is result is right, because I do not update my property ATTR..
Lets change the property:
>>> item.a = 20
>>> print item.a
20
But this result is wrong, it should be 23.021 and NOT 20 . It looks like my properties do not using the setter functions from its classes.
Why? What do I wrong in my code?
Edit: Sorry, I forgot to remove the inherited object of the ItemObject classes.. Now the code works.
For properties with setters and deleters to work properly, your classes need to inherit from object: Why does #foo.setter in Python not work for me?
You can just copy the property object itself over to the new class. It'll hold references to the getter, setter and deleter functions and there is no need to copy those across.
For new-style classes, your code is not working; you cannot assign to a class __dict__ attribute:
>>> item = SetProperties(Item01Object)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 4, in SetProperties
TypeError: 'dictproxy' object does not support item assignment
Use setattr() instead to set attributes on new-style classes:
def SetProperties( ItemObject ):
for propName, cls in AttrContainer.__dict__.iteritems():
if propName in ItemObject.properties:
setattr(ItemObject, propName, cls.__dict__[propName])
return ItemObject()
Note that the property object is copied across wholesale.
Demo:
>>> class Item01Object(object):
... properties = ["a","c"]
... ATTR = None
...
>>> def SetProperties( ItemObject ):
... for propName, cls in AttrContainer.__dict__.iteritems():
... if propName in ItemObject.properties:
... setattr(ItemObject, propName, cls.__dict__[propName])
... return ItemObject()
...
>>> item = SetProperties(Item01Object)
>>> item
<__main__.Item01Object object at 0x108205850>
>>> item.a
>>> item.a = 20
>>> item.a
23.021
You only have to copy across property objects to the target class once though; that your function returns an instance implies you are planning to use it for all instances created.
I'd make it a decorator instead:
def set_properties(cls):
for name, propcls in vars(AttrContainer).iteritems():
if name in cls.properties:
setattr(cls, name, vars(propcls)[name])
return cls
then use this on each of your Item*Object classes:
#set_properties
class Item01Object(object):
properties = ["a","c"]
ATTR = None
#set_properties
class Item02Object(object):
properties = ["b","c"]
ATTR = None
Demo:
>>> def set_properties(cls):
... for name, propcls in vars(AttrContainer).iteritems():
... if name in cls.properties:
... setattr(cls, name, vars(propcls)[name])
... return cls
...
>>> #set_properties
... class Item01Object(object):
... properties = ["a","c"]
... ATTR = None
...
>>> #set_properties
... class Item02Object(object):
... properties = ["b","c"]
... ATTR = None
...
>>> item01 = Item01Object()
>>> item01.c = 20
>>> item01.c
3
>>> item02 = Item02Object()
>>> item02.b = 42
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: can't set attribute
>>> item02.b
'Something'

behaviour of descriptor concept in python (confusing)

I understood python descriptor but I have a little confusion about this..
if you have a class descriptor as follows
class Descriptor(object):
def __get__(self, instance, owner):
print 'getting'
return self.value
def __set__(self, instance, value):
print 'setting'
self.value = value
def __delete__(self, instance):
print 'deleting'
del self.value
and a class whose attributes we want to manage is something like this..
class Test(object):
name = Descriptor()
def __init__(self, name):
print 'init test'
self.name = name
when I create object of class Test and do something it gives me answer like this...
t = Test('abc')
init test
setting
>>> t.name
getting
'abc'
>>> del t.name
deleting
>>> t
<__main__.Test object at 0x013FCCD0>
>>> t.name
getting
Now I want to have a class Test1 something like this..
class Test1(object):
def __init__(self, value):
print 'init test1'
self.name = Descriptor()
self. value = value
and if I create object of Test1 and try to access attribute of instance of Test1, I get output something like this..
t1 = Test1(12)
t1.name
>>> getting
>>> 12
>>> t1.name = 30
>>> setting
Q 1) my question is that is this name attribute declared in init of Test1, is bound to instance of Test1 or not... because when I try to get attribute dictionary of t1, it return empty dict...
t1.__dict__
>>> {}
same for class Test's instance t
t.__dict__
>>> {}
When I add a new attribute to any of these instances, like this...
t.some = 'some'
>>> t1.some = 'some'
and again if I try to access attribute dictionary it gives me only which I have added just now.. now all instance attribute
t.__dict__
>>> {'some': 'some'}
>>> t1.__dict__
>>> {'some': 'some'}
Q 2) So what is the difference between instance attributes defined in init (like variable name and value in class Descriptor and Test) and attributes defined after instance creation (like variable t.some).
Q 3) How class Test is different than class Test1.
In Test1 your Descriptor isn't really used as a descriptor, it's just a normal attribute called name, that happens to have some the special methods. But that doensn't really make it a descriptor yet.
If you read the docs about how descriptors are invoked, youll see the mechanism that is used to invoke the descriptors methods. In your case this would mean t.name woud be roughly equivalent to:
type(t).__dict__['name'].__get__(t, type(t))
and t1.name:
type(t1).__dict__['name'].__get__(t1, type(t1))
name is looked up in the __dict__ of the class, not of the instance, so that's where the difference is, Test1.__dict__ doesn't have a descriptor called name:
>>> Test.__dict__['name']
<__main__.Descriptor object at 0x7f637a57bc90>
>>> Test1.__dict__['name']
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 'name'
What you also should consider, is that your descriptor sets the value attribute on itself, that means all instances of Test will share the same value:
>>> t1 = Test(1)
init test
setting
>>> t2 = Test(2)
init test
setting
>>> t1.name
getting
2
>>> t2.name
getting
2
>>> t1.name = 0
setting
>>> t2.name
getting
0
I think that what yo acutally want to do is to set value on instance instead of self, that would get you the expected behaviour in Test.

Python: How to access parent class object through derived class instance?

I'm sorry for my silly question, but... let's suppose I have these classes:
class A():
msg = 'hehehe'
class B(A):
msg = 'hohoho'
class C(B):
pass
and an instance of B or C. How do I get the variable 'msg' from the parent's class object through this instance?
I've tried this:
foo = B()
print super(foo.__class__).msg
but got the message: "TypeError: super() argument 1 must be type, not classobj".
You actually want to use
class A(object):
...
...
b = B()
bar = super(b.__class__, b)
print bar.msg
Base classes must be new-style classes (inherit from object)
If the class is single-inherited:
foo = B()
print foo.__class__.__bases__[0].msg
# 'hehehe'
If the class is multiple-inherited, the question makes no sense because there may be multiple classes defining the 'msg', and they could all be meaningful. You'd better provide the actual parent (i.e. A.msg). Alternatively you could iterate through all direct bases as described in #Felix's answer.
Not sure why you want to do this
>>> class A(object):
... msg = 'hehehe'
...
>>> class B(A):
... msg = 'hohoho'
...
>>> foo=B()
>>> foo.__class__.__mro__[1].msg
'hehehe'
>>>
As msg is a class variable, you can just do:
print C.msg # prints hohoho
If you overwrite the variable (as you do in class B), you have to find the right parent class. Remember that Python supports multiple inheritance.
But as you define the classes and you now that B inherits from A you can always do this:
class B(A):
msg = 'hohoho'
def get_parent_message(self):
return A.msg
UPDATE:
The most reliable thing would be:
def get_parent_attribute(instance, attribute):
for parent in instance.__class__.__bases__:
if attribute in parent.__dict__:
return parent.__dict__[attribute]
and then:
foo = B()
print get_parent_attribute(foo, 'msg')
Try with:
class A(object):
msg = 'hehehe'
EDIT:
For the 'msg' attribute you would need:
foo = B()
bar = super(foo.__class__, foo)
print bar.msg
#for B() you can use __bases__
print foo.__class__.__bases__[0].msg
But this is not gonna be easy when there are multiple base classes and/or the depth of hierarchy is not one.

Categories

Resources