Explain __dict__ attribute - python

I am really confused about the __dict__ attribute. I have searched a lot but still I am not sure about the output.
Could someone explain the use of this attribute from zero, in cases when it is used in a object, a class, or a function?

Basically it contains all the attributes which describe the object in question. It can be used to alter or read the attributes.
Quoting from the documentation for __dict__
A dictionary or other mapping object used to store an object's (writable) attributes.
Remember, everything is an object in Python. When I say everything, I mean everything like functions, classes, objects etc (Ya you read it right, classes. Classes are also objects). For example:
def func():
pass
func.temp = 1
print(func.__dict__)
class TempClass:
a = 1
def temp_function(self):
pass
print(TempClass.__dict__)
will output
{'temp': 1}
{'__module__': '__main__',
'a': 1,
'temp_function': <function TempClass.temp_function at 0x10a3a2950>,
'__dict__': <attribute '__dict__' of 'TempClass' objects>,
'__weakref__': <attribute '__weakref__' of 'TempClass' objects>,
'__doc__': None}

Python documentation defines __dict__ as:
A dictionary or other mapping object used to store an object’s (writable) attributes.
This definition is however a bit fuzzy, which leads to a lot of wrong usage of __dict__.
Indeed, when you read this definition, can you tell what is a "writable" attribute and what it isn't?
Examples
Let's run a few examples showing how confusing and inaccurate it can be.
class A:
foo = 1
def __init__(self):
self.bar = 2
#property
def baz(self):
return self._baz
#bar.setter
def baz(self, value):
self._baz = value
>>> a = A()
>>> a.foo
1
>>> a.bar
2
Given the above class A and knowing __dict__'s definition, can you guess what would be the value of a.__dict__?
Is foo a writable attribute of a?
Is bar a writable attribute of a?
Is baz a writable attribute of a?
Is _baz a writable attribute of a?
Here is the answer:
>>> a.__dict__
{'bar': 2}
Surprisingly, foo doesn't show up. Indeed, although accessible with a.foo, it is an attribute of the class A, not of the instance a.
Now what happens if we define it explicitly as an attribute of a?
>>> a.foo = 1
>>> a.__dict__
{'bar': 2, 'foo': 1}
From our point of view, nothing really changed, a.foo is still equal to 1, but now it shows up in __dict__. Note that we can keep playing with it by deleting a.foo for instance:
>>> del a.foo
>>> a.__dict__
{'bar': 2}
>>> a.foo
1
What happened here is that we deleted the instance attribute, and calling a.foo falls back again to A.foo.
Let's have a look at baz now. We can assume that we can't find it in a.__dict__ because we didn't give it a value yet.
>>> a.baz = 3
Alright, now we defined a.baz. So, is baz a writable attribute? What about _baz?
>>> a.__dict__
{'bar': 2, '_baz': 3}
From __dict__'s perspective, _baz is a writable attribute, but baz isn't. The explanation, again, is that baz is an attribute of the class A, not the instance a.
>>> A.__dict__['baz']
<property at 0x7fb1e30c9590>
a.baz is only an abstraction layer calling A.baz.fget(a) behind the scenes.
Let's be even more sneaky with our dear friend and challenge its definition of "writable".
class B:
def __init__(self):
self.foobar = 'baz'
def __setattr__(self, name, value):
if name == 'foobar' and 'foobar' in self.__dict__:
# Allow the first definition of foobar
# but prevent any subsequent redefinition
raise TypeError("'foobar' is a read-only attribute")
super().__setattr__(name, value)
>>> b = B()
>>> b.foobar
'baz'
>>> b.foobar = 'bazbar'
TypeError: 'foobar' is a read-only attribute
>>> # From our developer's perspective, 'foobar' is not a writable attribute
>>> # But __dict__ doesn't share this point of view
>>> b.__dict__
{'foobar': 'baz'}
Then what is __dict__ exactly?
Thanks to the behavior noticed in the above examples, we can now have a better understanding of what __dict__ actually does. But we need to switch from the developer's to the computer's perspective:
__dict__ contains the data stored in the program's memory for this specific object.
That's it, __dict__ exposes what's actually stored in memory at our object's address.
Python documentation on data model also defines it as the object's namespace:
A class instance has a namespace implemented as a dictionary which is the first place in which attribute references are searched. When an attribute is not found there, and the instance’s class has an attribute by that name, the search continues with the class attributes.
However, I believe thinking of __dict__ as the object's memory table gives a much better visualization of what's included in this namespace, and what's not.
But! There is a catch...
You thought we were done with __dict__?
__dict__ is not the way to deal with the object's memory footprint, but a way.
There is indeed another way: __slots__. I won't detail here how it works, there is already a very complete answer about __slots__ if you want to learn more about it. But the important thing for us is that, if slots are defined:
class C:
__slots__ = ('foo', 'bar')
def __init__(self):
self.foo = 1
self.bar = 2
>>> c = C()
>>> c.foo
1
>>> c.bar
2
>>> c.__dict__
AttributeError: 'C' object has no attribute '__dict__'
We can say "goodbye" to __dict__.
So when should I use __dict__?
As we saw, __dict__ must be seen from the computer's perspective, not from the seat of the developer. More often than not, what we consider "attributes" of our object is not directly connected to what's actually stored in memory. Especially with the use of properties or __getattr__ for instance, that add a level of abstraction for our comfort.
Although the use of __dict__ to inspect the attributes of an object will work in most trivial cases, we can't rely 100% rely on it. Which is a shame for something used to write generic logic.
The use case of __dict__ should probably be limited to inspecting an object's memory contents. Which is not so common. And keep in mind that __dict__ might not be defined at all (or lack some attributes actually stored in memory) when slots are defined.
It can also be very useful in Python's console to quickly check a class' attributes and methods. Or an object's attributes (I know I just said we can't rely on it, but in the console who cares if it fails sometimes or if it's not accurate).
Thanks but... how do I browse my object's attribute then?
We saw that __dict__ is often misused and that we can't really rely on it to inspect an object's attributes. But then, what is the correct way to do it? Is there any way to browse an objects attributes from the developer's abstracted point of view?
Yes. There are several ways to do introspection, and the correct way will depend on what you actually want to get. Instance attributes, class attributes, properties, private attributes, even methods, ... Technically, all these are attributes, and according to your situation, you might want to include some but exclude others. The context is also important. Maybe you are using a library that already exposes the attributes you want through their API.
But in general, you can rely on the inspect module.
class D:
foo = 1
__slots = ('bar', '_baz')
#property
def baz(self):
return self._baz
#baz.setter
def baz(self, value):
self._baz = value
def __init__(self):
self.bar = 2
self.baz = 3
def pointless_method(self):
pass
>>> import inspect
>>> dict((k, v) for k, v in inspect.getmembers(d) if k[0] != '_')
{'bar': 2, 'baz': 3, 'foo': 1}
>>> dict((k, getattr(d, k)) for k, v in inspect.getmembers(D) if inspect.isdatadescriptor(v) or inspect.isfunction(v))
{
'__init__': <bound method D.__init__ of <__main__.D object at 0x7fb1e26a5b40>>,
'_baz': 3,
'bar': 2,
'baz': 3,
'pointless_method': <bound method D.pointless_method of <__main__.D object at 0x7fb1e26a5b40>>
}

__dict__ can get the instance variables (data attributes) in an object as a dictionary.
So, if there is Person class below:
class Person:
x1 = "Hello"
x2 = "World"
def __init__(self, name, age):
self.name = name
self.age = age
def test1(self):
pass
#classmethod
def test2(cls):
pass
#staticmethod
def test3():
pass
obj = Person("John", 27)
print(obj.__dict__) # Here
__dict__ gets name and age with their values in a dictionary as shown below:
{'name': 'John', 'age': 27}
And, if the new instance variable gender is added after instanciation as shown below:
# ...
obj = Person("John", 27)
obj.gender = "Male" # Here
print(obj.__dict__)
__dict__ gets name, age and gender with their values in a dictionary as shown below:
{'name': 'John', 'age': 27, 'gender': 'Male'}
In addition, if using dir() as shown below:
# ...
obj = Person("John", 27)
obj.gender = "Male"
print(dir(obj)) # Here
We can get all in an object as a list as shown below:
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
'__format__', '__ge__', '__getattribute__', '__gt__', '__hash__',
'__init__', '__init_subclass__', '__le__', '__lt__', '__module__',
'__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__',
'__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__',
'age', 'gender', 'name', 'test1', 'test2', 'test3', 'x1', 'x2']
And, as far as I researched and as I asked this question, there are no functions to get only the static or special variables or normal, class, static or special methods in an object in Python.

Related

Converting a python object with properties to a dictionary

I would like to know how to convert a python object from a dictionary (using python3 btw). I realize that this question has been asked (and answered) already (here). However, in my case the object is given entirely in terms of #property values, for example:
class Test:
#property
def value(self):
return 1.0
Regarding conversion to a dictionary: The __dict__ dictionary of the Test class is empty, and consequently, the vars
function does not work as expected:
>>> vars(Test())
{}
Still, I can use gettattr(Test(), 'value'), to obtain 1.0, so
the value is present.
Note: The reason I am coming up with this apparently contrived example is that I am trying to convert a cython cdef class (containing parameters) to a dictionary. The recommended way to wrap c structures with properties using cython is indeed based on properties.
I think you could use dir:
a = Test()
dir(a)
Output:
['__doc__', '__module__', 'value']
So you could maybe do something like:
d = {}
for attr in dir(a):
if not attr.startswith("__"):
d[attr] = getattr(a, attr)
Output:
d = {'value': 1.0}
Maybe you could abuse that:
In [10]: type(Test().__class__.__dict__['value']) is property
Out[10]: True
So you check the class of the object and if it has attribute of type property.
Here is how I would do it:
t = Test()
dictionary = {attr_name: getattr(t, attr_name)
for attr_name, method in t.__class__.__dict__.items()
if isinstance(method, property)}
It is even worse that that. You could imagine to build an instance __dict__ at init time, but that would not solve anything, except for read_only constant properties. Because the value in the dict will be a copy of the property at the time it was taken, and will not reflect future changes.

Dynamically adding __slots__ to an imported class [duplicate]

Suppose I have a class with __slots__
class A:
__slots__ = ['x']
a = A()
a.x = 1 # works fine
a.y = 1 # AttributeError (as expected)
Now I am going to change __slots__ of A.
A.__slots__.append('y')
print(A.__slots__) # ['x', 'y']
b = A()
b.x = 1 # OK
b.y = 1 # AttributeError (why?)
b was created after __slots__ of A had changed, so Python, in principle, could allocate memory for b.y. Why it didn't?
How to properly modify __slots__ of a class, so that new instances have the modified attributes?
You cannot dynamically alter the __slots__ attribute after creating the class, no. That's because the value is used to create special descriptors for each slot. From the __slots__ documentation:
__slots__ are implemented at the class level by creating descriptors (Implementing Descriptors) for each variable name. As a result, class attributes cannot be used to set default values for instance variables defined by __slots__; otherwise, the class attribute would overwrite the descriptor assignment.
You can see the descriptors in the class __dict__:
>>> class A:
... __slots__ = ['x']
...
>>> A.__dict__
mappingproxy({'__module__': '__main__', '__doc__': None, 'x': <member 'x' of 'A' objects>, '__slots__': ['x']})
>>> A.__dict__['x']
<member 'x' of 'A' objects>
>>> a = A()
>>> A.__dict__['x'].__get__(a, A)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: x
>>> A.__dict__['x'].__set__(a, 'foobar')
>>> A.__dict__['x'].__get__(a, A)
'foobar'
>>> a.x
'foobar'
You cannot yourself create these additional descriptors. Even if you could, you cannot allocate more memory space for the extra slot references on the instances produced for this class, as that's information stored in the C struct for the class, and not in a manner accessible to Python code.
That's all because __slots__ is only an extension of the low-level handling of the elements that make up Python instances to Python code; the __dict__ and __weakref__ attributes on regular Python instances were always implemented as slots:
>>> class Regular: pass
...
>>> Regular.__dict__['__dict__']
<attribute '__dict__' of 'Regular' objects>
>>> Regular.__dict__['__weakref__']
<attribute '__weakref__' of 'Regular' objects>
>>> r = Regular()
>>> Regular.__dict__['__dict__'].__get__(r, Regular) is r.__dict__
True
All the Python developers did here was extend the system to add a few more of such slots using arbitrary names, with those names taken from the __slots__ attribute on the class being created, so that you can save memory; dictionaries take more memory than simple references to values in slots do. By specifying __slots__ you disable the __dict__ and __weakref__ slots, unless you explicitly include those in the __slots__ sequence.
The only way to extend slots then is to subclass; you can dynamically create a subclass with the type() function or by using a factory function:
def extra_slots_subclass(base, *slots):
class ExtraSlots(base):
__slots__ = slots
ExtraSlots.__name__ = base.__name__
return ExtraSlots
It appears to me a type turns __slots__ into a tuple as one of it's first orders of action. It then stores the tuple on the extended type object. Since beneath it all, the python is looking at a tuple, there is no way to mutate it. Indeed, I'm not even sure you can access it unless you pass a tuple in to the instance in the first place.
The fact that the original object that you set still remains as an attribute on the type is (perhaps) just a convenience for introspection.
You can't modify __slots__ and expect to have that show up somewhere (and really -- from a readability perspective, You probably don't really want to do that anyway, right?)...
Of course, you can always subclass to extend the slots:
>>> class C(A):
... __slots__ = ['z']
...
>>> c = C()
>>> c.x = 1
>>> c.z = 1
You cannot modify the __slots__ attribute after class creation. This is because it would leade to strange behaviour.
Imagine the following.
class A:
__slots__ = ["x"]
a = A()
A.__slots__.append("y")
a.y = None
What should happen in this scenario? No space was originally allocated for a second slot, but according to the slots attribute, a should be able have space for y.
__slots__ is not about protecting what names can and cannot be accessed. Rather __slots__ is about reducing the memory footprint of an object. By attempting to modify __slots__ you would defeat the optimisations that __slots__ is meant to achieve.
How __slots__ reduces memory footprint
Normally, an object's attributes are stored in a dict, which requires a fair bit of memory itself. If you are creating millions of objects then the space required by these dicts becomes prohibitive. __slots__ informs the python machinery that makes the class object that there will only be so many attributes refered to by instances of this class and what the names of the attributes will be. Therefore, the class can make an optimisation by storing the attributes directly on the instance rather than in a dict. It places the memory for the (pointers to the) attributes directly on the object, rather than creating a new dict for the object.
Putting answers to this and related question together, I want to make an accent on a solution to this problem:
You can kind of modify __slots__ by creating a subclass with the same name and then replacing parent class with its child. Note that you can do this for classes declared and used in any module, not just yours!
Consider the following module which declares some classes:
module.py:
class A(object):
# some class a user should import
__slots__ = ('x', 'b')
def __init__(self):
self.b = B()
class B(object):
# let's suppose we can't use it directly,
# it's returned as a part of another class
__slots__ = ('z',)
Here's how you can add attributes to these classes:
>>> import module
>>> from module import A
>>>
>>> # for classes imported into your module:
>>> A = type('A', (A,), {'__slots__': ('foo',)})
>>> # for classes which will be instantiated by the `module` itself:
>>> module.B = type('B', (module.B,), {'__slots__': ('bar',)})
>>>
>>> a = A()
>>> a.x = 1
>>> a.foo = 2
>>>
>>> b = a.b
>>> b.z = 3
>>> b.bar = 4
>>>
But what if you receive class instances from some third-party module using the module?
module_3rd_party.py:
from module import A
def get_instance():
return A()
No problem, it will also work! The only difference is that you may need to patch them before you import third-party module (in case it imports classes from the module):
>>> import module
>>>
>>> module.A = type('A', (module.A,), {'__slots__': ('foo',)})
>>> module.B = type('B', (module.B,), {'__slots__': ('bar',)})
>>>
>>> # note that we import `module_3rd_party` AFTER we patch the `module`
>>> from module_3rd_party import get_instance
>>>
>>> a = get_instance()
>>> a.x = 1
>>> a.foo = 2
>>>
>>> b = a.b
>>> b.z = 3
>>> b.bar = 4
>>>
It works because Python imports modules only once and then shares them between all other modules, so the changes you make to modules affect all code running along yours.

How to dynamically change __slots__ attribute?

Suppose I have a class with __slots__
class A:
__slots__ = ['x']
a = A()
a.x = 1 # works fine
a.y = 1 # AttributeError (as expected)
Now I am going to change __slots__ of A.
A.__slots__.append('y')
print(A.__slots__) # ['x', 'y']
b = A()
b.x = 1 # OK
b.y = 1 # AttributeError (why?)
b was created after __slots__ of A had changed, so Python, in principle, could allocate memory for b.y. Why it didn't?
How to properly modify __slots__ of a class, so that new instances have the modified attributes?
You cannot dynamically alter the __slots__ attribute after creating the class, no. That's because the value is used to create special descriptors for each slot. From the __slots__ documentation:
__slots__ are implemented at the class level by creating descriptors (Implementing Descriptors) for each variable name. As a result, class attributes cannot be used to set default values for instance variables defined by __slots__; otherwise, the class attribute would overwrite the descriptor assignment.
You can see the descriptors in the class __dict__:
>>> class A:
... __slots__ = ['x']
...
>>> A.__dict__
mappingproxy({'__module__': '__main__', '__doc__': None, 'x': <member 'x' of 'A' objects>, '__slots__': ['x']})
>>> A.__dict__['x']
<member 'x' of 'A' objects>
>>> a = A()
>>> A.__dict__['x'].__get__(a, A)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: x
>>> A.__dict__['x'].__set__(a, 'foobar')
>>> A.__dict__['x'].__get__(a, A)
'foobar'
>>> a.x
'foobar'
You cannot yourself create these additional descriptors. Even if you could, you cannot allocate more memory space for the extra slot references on the instances produced for this class, as that's information stored in the C struct for the class, and not in a manner accessible to Python code.
That's all because __slots__ is only an extension of the low-level handling of the elements that make up Python instances to Python code; the __dict__ and __weakref__ attributes on regular Python instances were always implemented as slots:
>>> class Regular: pass
...
>>> Regular.__dict__['__dict__']
<attribute '__dict__' of 'Regular' objects>
>>> Regular.__dict__['__weakref__']
<attribute '__weakref__' of 'Regular' objects>
>>> r = Regular()
>>> Regular.__dict__['__dict__'].__get__(r, Regular) is r.__dict__
True
All the Python developers did here was extend the system to add a few more of such slots using arbitrary names, with those names taken from the __slots__ attribute on the class being created, so that you can save memory; dictionaries take more memory than simple references to values in slots do. By specifying __slots__ you disable the __dict__ and __weakref__ slots, unless you explicitly include those in the __slots__ sequence.
The only way to extend slots then is to subclass; you can dynamically create a subclass with the type() function or by using a factory function:
def extra_slots_subclass(base, *slots):
class ExtraSlots(base):
__slots__ = slots
ExtraSlots.__name__ = base.__name__
return ExtraSlots
It appears to me a type turns __slots__ into a tuple as one of it's first orders of action. It then stores the tuple on the extended type object. Since beneath it all, the python is looking at a tuple, there is no way to mutate it. Indeed, I'm not even sure you can access it unless you pass a tuple in to the instance in the first place.
The fact that the original object that you set still remains as an attribute on the type is (perhaps) just a convenience for introspection.
You can't modify __slots__ and expect to have that show up somewhere (and really -- from a readability perspective, You probably don't really want to do that anyway, right?)...
Of course, you can always subclass to extend the slots:
>>> class C(A):
... __slots__ = ['z']
...
>>> c = C()
>>> c.x = 1
>>> c.z = 1
You cannot modify the __slots__ attribute after class creation. This is because it would leade to strange behaviour.
Imagine the following.
class A:
__slots__ = ["x"]
a = A()
A.__slots__.append("y")
a.y = None
What should happen in this scenario? No space was originally allocated for a second slot, but according to the slots attribute, a should be able have space for y.
__slots__ is not about protecting what names can and cannot be accessed. Rather __slots__ is about reducing the memory footprint of an object. By attempting to modify __slots__ you would defeat the optimisations that __slots__ is meant to achieve.
How __slots__ reduces memory footprint
Normally, an object's attributes are stored in a dict, which requires a fair bit of memory itself. If you are creating millions of objects then the space required by these dicts becomes prohibitive. __slots__ informs the python machinery that makes the class object that there will only be so many attributes refered to by instances of this class and what the names of the attributes will be. Therefore, the class can make an optimisation by storing the attributes directly on the instance rather than in a dict. It places the memory for the (pointers to the) attributes directly on the object, rather than creating a new dict for the object.
Putting answers to this and related question together, I want to make an accent on a solution to this problem:
You can kind of modify __slots__ by creating a subclass with the same name and then replacing parent class with its child. Note that you can do this for classes declared and used in any module, not just yours!
Consider the following module which declares some classes:
module.py:
class A(object):
# some class a user should import
__slots__ = ('x', 'b')
def __init__(self):
self.b = B()
class B(object):
# let's suppose we can't use it directly,
# it's returned as a part of another class
__slots__ = ('z',)
Here's how you can add attributes to these classes:
>>> import module
>>> from module import A
>>>
>>> # for classes imported into your module:
>>> A = type('A', (A,), {'__slots__': ('foo',)})
>>> # for classes which will be instantiated by the `module` itself:
>>> module.B = type('B', (module.B,), {'__slots__': ('bar',)})
>>>
>>> a = A()
>>> a.x = 1
>>> a.foo = 2
>>>
>>> b = a.b
>>> b.z = 3
>>> b.bar = 4
>>>
But what if you receive class instances from some third-party module using the module?
module_3rd_party.py:
from module import A
def get_instance():
return A()
No problem, it will also work! The only difference is that you may need to patch them before you import third-party module (in case it imports classes from the module):
>>> import module
>>>
>>> module.A = type('A', (module.A,), {'__slots__': ('foo',)})
>>> module.B = type('B', (module.B,), {'__slots__': ('bar',)})
>>>
>>> # note that we import `module_3rd_party` AFTER we patch the `module`
>>> from module_3rd_party import get_instance
>>>
>>> a = get_instance()
>>> a.x = 1
>>> a.foo = 2
>>>
>>> b = a.b
>>> b.z = 3
>>> b.bar = 4
>>>
It works because Python imports modules only once and then shares them between all other modules, so the changes you make to modules affect all code running along yours.

Python Descriptor's - Documentation unclear

I was looking at python's descriptor's documentation here, and the statement which got me thinking is:
For objects, the machinery is in object.__getattribute__() which transforms b.x into type(b).__dict__['x'].__get__(b, type(b))
under a section named Invoking Descriptors.
Last part of the statement b.x into type(b).__dict__['x'].__get__(b, type(b)) is causing the conflict here. As per my understanding, if we lookup for attribute on an instance, then instance.__dict__is being looked up, and if we didn't find anything type(instance).__dict__ is referred.
In our example, b.x should then be evaluated as:
b.__dict__["x"].__get__(b, type(b)) instead of
type(b).__dict__['x'].__get__(b, type(b))
Is this understanding correct? Or am I going wrong somewhere in interpretation?
Any explanation would be helpful.
Thanks.
I am adding the second part as well:
Why instance attributes does not respect the descriptor protocol? For ex: referring to code below:
>>> class Desc(object):
... def __get__(self, obj, type):
... return 1000
... def __set__(self, obj, value):
... raise AttributeError
...
>>>
>>> class Test(object):
... def __init__(self,num):
... self.num = num
... self.desc = Desc()
...
>>>
>>> t = Test(10)
>>> print "Desc details are ", t.desc
Desc details are <__main__.Desc object at 0x7f746d647890>
Thanks for helping me out.
Your understanding is incorrect. x most likely does not appear in the instance's dict at all; the descriptor object appears in the class's dict or the dict of one of the superclasses.
Let's use an example:
class Foo(object):
#property
def x(self):
return 0
def y(self):
return 1
x = Foo()
x.__dict__['x'] = 2
x.__dict__['y'] = 3
Foo.x and Foo.y are both descriptors. (Properties and functions both implement the descriptor protocol.)
When we access x.x:
>>> x.x
0
We do not get the value from x's dict. Instead, since Python finds a data descriptor by the name of x in Foo.__dict__, it calls
Foo.__dict__['x'].__get__(x, Foo)
and returns the result. The data descriptor wins over the instance dict.
On the other hand, if we try x.y:
>>> x.y
3
we get 3, rather than a bound method object. Functions don't have __set__ or __delete__, so the instance dict overrides them.
As for the new Part 2 to your question, descriptors don't function in the instance dict. Consider what would happen if they did:
class Foo(object):
#property
def bar(self):
return 4
Foo.bar = 3
If descriptors functioned in the instance dict, then the assignment to Foo.bar would find a descriptor in Foo's dict and call Foo.__dict__['bar'].__set__. The __set__ method of the descriptor would have to handle setting the attribute on both the class and the instance, and it would have to tell the difference somehow, even in the face of metaclasses. There just isn't a compelling reason to complicate the protocol this way.

Dynamic variable creation [duplicate]

This question already has answers here:
How can you dynamically create variables? [duplicate]
(8 answers)
Closed 8 years ago.
My question is how to create variables "on the fly". I'm trying to generate an object with a random set of attributes.
from random import randint, choice
class Person(object):
def __init__(self):
self.attributes = []
possible_attributes= ['small', 'black', 'scary', 'smelly', 'happy'] # idk, random
chance = randint(1,5)
chosen_attributes = []
for i in xrange(chance):
#psuedo code...
local_var = choice(possible_attributes)
if local_var not in chosen_attributes:
VAR = local_var # VAR needs to be dynamic and 'global' for use later on
chosen_attributes.append(local_var)
Person.attributes.append(local_var)
I'm positive how I'm wanting to do this is not a good way, so if anyone understand what I'm looking for and can offer a better method (one that works, for starters) I would be most appreciative.
To add attributes to an instance of the class you can use .__dict__:
class Person(object):
def addattr(self,x,val):
self.__dict__[x]=val
output:
>>> a=Person()
>>> a.addattr('foo',10)
>>> a.addattr('bar',20)
>>> a.foo
10
>>> a.bar
20
>>> b=Person()
>>> b.addattr('spam','somevalue')
>>> b.spam
'somevalue'
Well Im not sure what you are tying to achieve here, but there might be a couple things off here ....
class Person(object):
def __init__(self):
self.attributes = []
This defines a class object from which you can create objects that contain attributes as we can see from the __init__ or constructor as its called in some other languages.
but you have
Person.attributes.append(local_var)
here Person is a class object it really doesn't have the attributes that where define since does are only for objects created from this class ...
you can create a new person and add the appropriate attributes to them.
>>> me = Person()
>>> me.attributes.append('humble')
Second we really shouldn't access attributes directly since anything could be appended to a list, including duplicate attributes.
so why not simply add a function that adds attributes.
class Person(object):
def __init__(self):
self._attributes = [] # note any variable starting with _ is meant to be private
def add_attribute(attribute):
if attribute not in self._attributes:
self._attributes.append(attribute)
if performance is a concern we can use a set() instead of [] since it won't allow duplicate entries and really fast checks for any lookups the downside is that the values have to be hash-able ie strings or ints or other simple data types ...
Use vars. For example:
>>> import math
>>> class foo: pass
>>> vars(foo)
{'__module__': '__main__', '__doc__': None}
>>> vars(foo)['fn']=math.sin
>>> foo.fn(2)
0.9092974268256817
>>> vars(foo)
{'__module__': '__main__', '__doc__': None, 'fn': <built-in function sin>}
As you know, methods are just functions with one more argument.
While I can't really tell what you need this for, I am going to guess that you don't actually want global variables, just a single Person object that has a random/dynamic set of attributes. For that, you want setattr and random.sample():
crazy_person = Person()
selected_attribute_names = random.sample(possible_attributes, 3)
for attribute_name in selected_attribute_names:
setattr(crazy_person, attribute_name, True)
# optional -- if you want to have a list of attributes, just copy that in:
crazy_person.attributes = selected_attribute_names
# vars(crazy_person).keys() will give you more or less the same thing though, for free
# hasattr(crazy_person, 'small') will tell you if you picked 'small'
# if we chose 'small', then from here on, crazy_person.small will be True as well
This is how I might construct objects with a random set of attributes, given your definitions:
import random
possible_attributes= ['small', 'black', 'scary', 'smelly', 'happy']
class Person(object):
def __init__(self):
self.attributes = random.sample(possible_attributes, random.randint(0, len(possible_attributes)))
This selects a random number of attributes from your list of possible attributes.
>>> print Person().attributes
['small', 'smelly']
>>> print Person().attributes
['scary', 'smelly', 'happy']
Generally, instead of polluting the global namespace, you do not want to add to globals. Dictionaries provide a namespace-like way to access everything, but if you really have to, use globals()[x] =something to modify the item, or perhaps use the very nasty
exec "var_name=somehting"

Categories

Resources