Python : Behavior of class and instance variables - python

I have following two code samples
Example 1:
class MyClass(object):
def __init__(self, key, value):
self._dict = self._dict.update({key:value})
m = MyClass('ten',10)
print m._dict
Output:
AttributeError: 'MyClass' object has no attribute '_dict'
Example2:
class MyClass(object):
_dict = {}
def __init__(self, key, value):
self._dict = self._dict.update({key:value})
m = MyClass('ten',10)
print m._dict
Output:
None
I am quite surprised with above behavior
Why the example2 compiled successfully by just addition of _dict = {}
line, and line present at class scope.
also why None output?
I believed class scope variables has no relation with instance variable
(special with self)
Any Explaination?

Your 'example 2' defines a single dictionary at the class level. All instances of the class will share that same dictionary, at least unless you reassign _dict on the instance.
See this question for a detailed explanation:
Why do attribute references act like this with Python inheritance?
As for why you're getting None - the update method changes its dict in place, and returns None.

The None output is because dict.update returns None. It modifies the dictionary itself, but does not return anything. So you probably wanted self._dict.update({key:value}). However, self._dict doesn't exist at initialization. So it would make more sense to do self._dict = {key: value}. If you're trying to modify the object's internal dictionary, then you should do self.__dict__.update({key:value}). However, this is bad practice. A better idea would be to write setattr(self, key, value). The reason Example2 is working successfully is because if you try to do getattr(instance, thing) (which is what instance.thing does), and thing is not in instance.__dict__, then instance.__class__.__dict__ will be checked instead.

Because the _dict in Example 2 is a class variable so it's an attribute of MyClass where as the _dict in Example 1 is an instance variable so it's a instance attribute.

Example 1: you are trying to update an object that is yet to be created. therefore error.
Example 2: When working in the inner scope of the function, if you modify the variable it makes changes to the previously defined _dict. But if you assign the value, it makes a new variable with the same name in the inner scope.
This will work.
class MyClass(object):
_dict = {}
def __init__(self, key, value):
self._dict.update({key:value})
This will not.
class MyClass(object):
_dict = {}
def __init__(self, key, value):
self._dict = self._dict.update({key:value})
because you are doing an assigning operation. It makes a new variable. So no changes are made to the _dict in the outer scope. Your _dict in the outer scope is still empty and returns None.

self._dict does not yet exist, so the first version raises that exception. The second one actually falls through looking _dict up on the instance and instead updates the class attribute, then assigns the class-level dictionary to the instance-scope _dict attribute.

Related

Returning the inner namespace of a class as dictionary

Imagine we have a class like as below:
class P1(object):
def __init__(self,value=0,src='admin'):
self.value=value
self.src=src
Is it possible to add a method that returns the name inner variables as a dictionary with name as the name and the value as the value like this:
def getDict(self):
return dictionary(variables)
which returns:
{
'value':0,
'src':'admin'
}
PS I know it can be down by hard coding it, I am asking if there is Pythonic way of doing it with one method call.
You can use __dict__:
A dictionary or other mapping object used to store an object’s (writable) attributes.
def getDict(self):
return self.__dict__
or vars() builtin, which simply returns the __dict__ attribute:
return vars(self)
Warning: Note that this returns a reference to the actual namespace, and any changes you make to the dictionary will be reflected to the instance.
You're looking for vars example:
return vars(self)
Output:
{'value': 0, 'src': 'admin'}
Yes, you can use magic methods. If not create your own method which returns the dict of value and src. Your code:
class P1(object):
def __init__(self,value=0,src='admin'):
self.value=value
self.src=src
def getdict(self):
return {"value":self.value,"src":self.src} #manual return of dict
x=P1(5,"user") #checking for non default values
print(x.getdict())
x=P1() #checking for default values
print(x.getdict())

Creating a global variable passed as parameter

I'm trying to create a function, that would take a parameter, then make a global variable out of it(more precisely an instance of a class).
class SomeClass():
#some stuff defined inside
def create(crt, **kwargs):
globals()[crt] = SomeClass()
for key, value in kwargs.items():
if crt.__dict__.__contains__(key):
crt.__setattr__(key, value)
return crt
Output that I'm interested in would be:
create(foo, class_attribute=10)
That would then allow me to:
foo.other_attribute = "whatever"
I can't pass a parameter without '' if it's not defined earlier, neither can I pass a string, because it's not a variable in itself, hence it can't be an instance of a class.
Would that be even possible?
This is a bad idea, but here's how to do it.
You need to pass the name as a string. When you're setting the attributes, do it on a local variable that contains the new object.
def create(crt, **kwargs):
obj = SomeClass()
for key, value in kwargs.items():
if obj.__dict__.__contains__(key):
obj.__setattr__(key, value)
globals()[crt] = obj
return obj
create('foo', class_attribute=10)
foo.other_attribute = 'whatever'

Dynamically set dict elements with getattr

I already see the post Dynamically get dict elements via getattr?, but i can't solve my problem.
I want do something similar, but I'm a bit confused. I want set (not get) the data in the correspondent dictionary, but I get this error
AttributeError: type object 'Dictionary' has no attribute 'VERBS'.
My code is:
class Dictionary:
def __init__(self):
self.VERBS = dict()
self.REFERENCER = dict()
def setDictionary(self, fileDictionary, name):
methodCaller = Dictionary()
dictionary = "self."+name.upper()
dictionary = getattr(Dictionary, name.upper())
dictionary = fileDictionary.copy()
Can you see what I'm doing wrong? Because I don't understand completely that.
I think that this is what you are looking for:
class Dictionary:
def __init__(self):
self.VERBS = dict()
self.REFERENCER = dict()
def setDictionary(self, fileDictionary, name):
setattr(self, name.upper(), fileDictionary)
This uses setattr to assign fileDictionary to the member with the name name.upper() on self
The error that the code in the question has results from attempting to access the name on the class where it doesn't exist rather than on the instance where it exists.
It is also possible to write the method as:
def setDictionary(self, fileDictionary, name):
dictionary = getattr(self, name.upper())
dictionary.update(fileDictionary)
Which might be closer to what you were attempting.
Note that these two behave differently if the passed dictionary is mutated. The first binds the object to the name on the instance. The second updates the existing dictionary with the items from the passed dictionary.

how to create class variable dynamically in python

I need to make a bunch of class variables and I would like to do it by looping through a list like that:
vars=('tx','ty','tz') #plus plenty more
class Foo():
for v in vars:
setattr(no_idea_what_should_go_here,v,0)
is it possible? I don't want to make them for an instance (using self in the __init__) but as class variables.
You can run the insertion code immediately after a class is created:
class Foo():
...
vars=('tx', 'ty', 'tz') # plus plenty more
for v in vars:
setattr(Foo, v, 0)
Also, you can dynamically store the variable while the class is being created:
class Bar:
locals()['tx'] = 'texas'
Late to the party but use the type class constructor!
Foo = type("Foo", (), {k: 0 for k in ("tx", "ty", "tz")})
If for any reason you can't use Raymond's answer of setting them up after the class creation then perhaps you could use a metaclass:
class MetaFoo(type):
def __new__(mcs, classname, bases, dictionary):
for name in dictionary.get('_extra_vars', ()):
dictionary[name] = 0
return type.__new__(mcs, classname, bases, dictionary)
class Foo(): # For python 3.x use 'class Foo(metaclass=MetaFoo):'
__metaclass__=MetaFoo # For Python 2.x only
_extra_vars = 'tx ty tz'.split()
The locals() version did not work for me in a class.
The following can be used to dynamically create the attributes of the class:
class namePerson:
def __init__(self, value):
exec("self.{} = '{}'".format("name", value)
me = namePerson(value='my name')
me.name # returns 'my name'
setattr(object, name, value)
This is the counterpart of getattr(). The arguments are an object, a string and an arbitrary value. The string may name an existing attribute or a new attribute. The function assigns the value to the attribute, provided the object allows it.
For example, setattr(x, 'name', value) is equivalent to x.name = value.
The function you need is:
setattr(obj, name, value)
This allows you to set named attributes for a given class (this can be self).
The built in documentation for this function is pretty self-explanatory:
Signature: setattr(obj, name, value, /)
Docstring:
Sets the named attribute on the given object to the specified value.
setattr(x, 'y', v) is equivalent to ``x.y = v''
Type: builtin_function_or_method
Example use
One use of this is to use a dictionary to set multiple class attributes, in my case this was from xpath definitions. I felt this improved maintainability by keeping potentially more fragile xpath definitions all in one place:
class Person:
def _extract_fields(self):
''' Process the page using XPath definitions '''
logging.debug("_extract_fields(): {}".format(repr(self)))
# describe how to extract data from the publicly available site
# (kept together for maintainability)
fields = {
'staff_name':
'//div[#id="staff_name"]//text()',
'staff_dob':
'(//div[#id="staff_dob"]//text())[1]'
}
# populate named class attributes from the dict
for key in fields:
setattr(self, key, self._parsed_content.xpath(fields[key]))
def __init__(self):
self._extract_fields()

Get class and object attributes of class without methods and builtins

Say I have this class:
class MyClass(object):
my_attrib = 'foo'
my_other_attrib = 'bar'
def mymethod():
pass
Now how can I get ONLY the attributes of the class MyClass, WITHOUT methods and builtins like __dict__ and so on?
I want to get a dictionary like {'my_attrib':'foo', 'my_other_attrib':'bar'}, when applied to the class above.
You can filter out everything you don't need from __dict__:
def getAttributes(clazz):
return {name: attr for name, attr in clazz.__dict__.items()
if not name.startswith("__")
and not callable(attr)
and not type(attr) is staticmethod}
Edit: An alternative that behaves slightly differently for class properties and descriptors:
def getAttributes2(clazz):
attrs = {}
for name in vars(clazz):
if name.startswith("__"):
continue
attr = getattr(clazz, name)
if callable(attr):
continue
attrs[name] = attr
return attrs
(In practice, this should be rarely different from the first version.)
This should get you close:
import inspect
class MyClass(object):
my_attrib = 'foo'
my_other_attrib = 'bar'
def mymethod():
pass
for name, value in inspect.getmembers(MyClass):
if not inspect.ismethod(value) and not name.startswith('__'):
print name
This outputs:
my_attrib
my_other_attrib
NOTE - There may be a better / more-official way to do this, but this should point you in the right direction.
__dict__ gives you all that but you could use a C extension maybe to get what you want. Not sure why you would do that though.
You can use types (doc) to distinguish between members of __dict__.
You can use the builtin dir() to get everything, then filter. You will not need the inspect module.
def get_attrs_without_methods(klass):
attrs = dir(klass)
d = {}
for x in attrs:
if x.startswith('__'): continue
value = getattr(self,x)
if not callable(value):
d[x] = value
return d
Sometimes, you may want to get ONLY class variables instead of class variables AND instance variable.
You can filter out instance variables by relying on __dict__. Or you can get the attributes using __class__ and filter out the methods. __class__ does not return instance variables.
#after collecting your attributes using the above example...
for attr, value in vars(obj).items():
d.pop(attr) #remove instance variables from the dict
#both vars(obj).items() and obj.__dict__.items() return similar iterable.
Note that if the object implementation overrides __dict__ and returns None, vars(obj) and obj.__dict__.items() will not return a dictionary.

Categories

Resources