Python class members with dynamic names - python

I've seen a class in python which does something of this kind:
obj = Class( name = "somename" )
obj.somename = something
Namely the class initialisation created a member called as the argument string.
I cannot manage to reproduce this behaviour. Any ideas?
I managed to do something similar using globals()["name"] = value. But in this case the created object is son of the module not of the "Class" object. And it's callable as module.somename instead of obj.somename.

You can easily create a dynamically named member by using setattr:
>>> class Foo(object):
... def __init__(self, name):
... setattr(self, name, 42)
...
>>> f = Foo('bar')
>>> f.bar
42
>>> f.bar = 'hello'
>>> f.bar
'hello'
Note however, that just setting any attribute on an instance of a class is possible for any regular class (that doesn't define __slots__):
>>> class Qux(object):
... pass
...
>>> q = Qux()
>>> q.foobar = 'hello'
>>> q.foobar
'hello'

You can either create a dynamically named fixed value property like this:
class MyClass():
def __init__(self, attr):
self.__dict__[attr] = 'some_value'
Or create as many properties as the calling function wants:
class MyClass():
def __init__(self, *argv, **kwargs):
for key,value in kwargs.items():
self.__dict__[key] = value

Related

Python: Only allow attributes to be set that have a #property decorator

class MyClass():
def __init__(self):
self.attribute_1 = "foo"
self.attribute_2 = "bar"
#property
def attribute_1(self):
return self._attribute_1
#attribute_1.setter
def attribute_1(self,s):
self._attribute_1 = s
#property
def attribute_2(self):
return self._attribute_2
#attribute_2.setter
def attribute_2(self,s):
self._attribute_2 = s
>>> ob = MyClass()
>>> ob.attribute_1 = 'fizz' #Ok
>>> ob.atribute_1 = 'buzz' #want to throw an exception because this has no setter or #property def
I would like my class to complain if we try and set an attribute that has not been decorated with property and a setter. I have tried using slots but can't get it working with the property decorator. 'attribute' in __slots__ conflicts with class variable
Any thoughts?
__slots__ should contain all instance variables, in your case it is _attribute_1 and _attribute_2 (the ones with underscores used internally) so just do that:
class MyClass():
__slots__ = ["_attribute_1", "_attribute_2"]
pass # rest of implementation
note that if your property is just directly forwarding you might as well just put the public variables in the slots and only have properties for fields that need more validation or other logic. having slots is effectively a property really:
>>> MyClass._attribute_1
<member '_attribute_1' of 'MyClass' objects>

How to override assignment of class attributes

I am trying to create a class that returns the class name together with the attribute. This needs to work both with instance attributes and class attributes
class TestClass:
obj1 = 'hi'
I.e. I want the following (note: both with and without class instantiation)
>>> TestClass.obj1
('TestClass', 'hi')
>>> TestClass().obj1
('TestClass', 'hi')
A similar effect is obtained when using the Enum package in python, but if I inherit from Enum, I cannot create an __init__ function, which I want to do as well
If I use Enum I would get:
from enum import Enum
class TestClass2(Enum):
obj1 = 'hi'
>>> TestClass2.obj1
<TestClass2.obj1: 'hi'>
I've already tried overriding the __getattribute__ magic method in a meta class as suggested here: How can I override class attribute access in python. However, this breaks the __dir__ magic method, which then wont return anything, and furthermore it seems to return name of the meta class, rather than the child class. Example below:
class BooType(type):
def __getattribute__(self, attr):
if attr == '__class__':
return super().__getattribute__(attr)
else:
return self.__class__.__name__, attr
class Boo(metaclass=BooType):
asd = 'hi'
>>> print(Boo.asd)
('BooType', 'asd')
>>> print(dir(Boo))
AttributeError: 'tuple' object has no attribute 'keys'
I have also tried overriding the __setattr__ magic method, but this seems to only affect instance attributes, and not class attributes.
I should state that I am looking for a general solution. Not something where I need to write a #property or #classmethod function or something similar for each attribute
I got help from a colleague for defining meta classes, and came up with the following solution
class MyMeta(type):
def __new__(mcs, name, bases, dct):
c = super(MyMeta, mcs).__new__(mcs, name, bases, dct)
c._member_names = []
for key, value in c.__dict__.items():
if type(value) is str and not key.startswith("__"):
c._member_names.append(key)
setattr(c, key, (c.__name__, value))
return c
def __dir__(cls):
return cls._member_names
class TestClass(metaclass=MyMeta):
a = 'hi'
b = 'hi again'
print(TestClass.a)
# ('TestClass', 'hi')
print(TestClass.b)
# ('TestClass', 'hi again')
print(dir(TestClass))
# ['a', 'b']
Way 1
You can use classmethod decorator to define methods callable at the whole class:
class TestClass:
_obj1 = 'hi'
#classmethod
def obj1(cls):
return cls.__name__, cls._obj1
class TestSubClass(TestClass):
pass
print(TestClass.obj1())
# ('TestClass', 'hi')
print(TestSubClass.obj1())
# ('TestSubClass', 'hi')
Way 2
Maybe you should use property decorator so the disered output will be accessible by instances of a certain class instead of the class itself:
class TestClass:
_obj1 = 'hi'
#property
def obj1(self):
return self.__class__.__name__, self._obj1
class TestSubClass(TestClass):
pass
a = TestClass()
b = TestSubClass()
print(a.obj1)
# ('TestClass', 'hi')
print(b.obj1)
# ('TestSubClass', 'hi')

Meaning of self.__dict__ = self in a class definition

I'm trying to understand the following snippet of code:
class Config(dict):
def __init__(self):
self.__dict__ = self
What is the purpose of the line self.__dict__ = self? I suppose it overrides the default __dict__ function with something that simply returns the object itself, but since Config inherits from dict I haven't been able to find any difference with the default behavior.
Assigning the dictionary self to __dict__ allows attribute access and item access:
>>> c = Config()
>>> c.abc = 4
>>> c['abc']
4
As per Python Document, object.__dict__ is:
A dictionary or other mapping object used to store an object’s (writable) attributes.
Below is the sample example:
>>> class TestClass(object):
... def __init__(self):
... self.a = 5
... self.b = 'xyz'
...
>>> test = TestClass()
>>> test.__dict__
{'a': 5, 'b': 'xyz'}

Set dictionary key to attribute of class instance

I have a dictionary which stores objects of a class foo. Class foo has an attribute Name. For every instance, I want the Name attribute to be the key to the instance in the dictionary. All instances of the class will be defined inside the dictionary.
class foo:
def __init__(self):
self.Name = None #self.Name should equal "self"
foo_dict = {
'foo1' = foo()
}
#(foo.Name should equal 'foo1')
How can I set the Name attribute to be the key to the instance in the dictionary?
Comment if specifications are needed.
I can't possibly stress enough how BAD this is... Please, please, use this only for educational purposes. It's crumbly, unreliable... BAD If you change anything in your code, it'll stop working. It is dirty. It is possibly non portable... OMG... I think a few kittens were killed when I hit Post Your Answer
import inspect
import re
class Foo(object):
def __init__(self):
r = re.compile(
r"\W+['\"](?P<name>\w+)['\"]\W+%s\W+"
% self.__class__.__name__
)
caller_frame = inspect.currentframe().f_back
code_context = inspect.getframeinfo(caller_frame).code_context
match = r.match(''.join(code_context))
if match:
self.name = match.groupdict()['name']
print "Assigned name: %s" % self.name
else:
raise Exception("This wasn't called as it was supposed to")
if __name__ == "__main__":
foo_dict = {
'foo1': Foo(),
'foo2': Foo(),
}
But it does what you seem to be asking:
borrajax#borrajax:/tmp$ python ./test.py
Assigned name: foo1
Assigned name: foo2
Now, what I would do is:
Option 1:
Pass the name in the initialization.
Possibly the simplest, most maintainable and that leaves the code in a much clearer state (important if someone else reads your code)
class Foo(object):
def __init__(self, name):
self.name = name
print "Assigned name: %s" % self.name
if __name__ == "__main__":
foo_dict = {
'foo1': Foo('foo1'),
'foo2': Foo('foo2'),
}
Option 2:
Create your own dict class and overwrite the __setitem__ method (see also Subclassing Python dictionary to override __setitem__ and How to "perfectly" override a dict?):
class Foo(object):
pass
class MyDict(dict):
def __setitem__(self, key, val):
if not isinstance(val, Foo):
raise TypeError("My dict only accepts %s" % Foo)
val.name = key
print "Assigned name: %s" % val.name
return super(MyDict, self).__setitem__(key, val)
if __name__ == "__main__":
foo_dict = MyDict()
foo_dict['foo1'] = Foo()
foo_dict['foo2'] = Foo()
foo_dict['foo3'] = 1
Prints:
borrajax#borrajax:/tmp$ python ./test.py
Assigned name: foo1
Assigned name: foo2
Traceback (most recent call last):
File "./stack64.py", line 17, in <module>
foo_dict['foo3'] = 1
File "./stack64.py", line 8, in __setitem__
raise TypeError("My dict only accepts %s" % Foo)
TypeError: My dict only accepts <class '__main__.Foo'>
This has the disadvantage of magically adding attributes (the .name) to the instances of Foo when assigned to the dictionary, which can cause name conflicts (if your Foo class already had a .name, this method would change its value). In general, I'd stay away of methods that magically add attributes to instances in the middle of the execution.
Option 3:
Use #Daniel's answer to this question. Clean and understandable for someone else reading your code.
Seems like you need a reference to the instance to do what you want. If you build the dictionary with a comprehension, you can create instance references and use them.
class Foo(object):
def __init__(self, n = None):
self.name = n
d = {f.name:f for f in (Foo(n) for n in 'abcd')}
>>> d
{'a': <__main__.Foo object at 0x03DF9710>, 'c': <__main__.Foo object at 0x03E01250>, 'b': <__main__.Foo object at 0x03DF9A50>, 'd': <__main__.Foo object at 0x03E01290>}
>>>
>>> d = {f.name:f for f in (Foo(n) for n in [1])}
>>> d
{1: <__main__.Foo object at 0x03E01B50>}
>>> foo_dict = {}
>>> foo_dict.update(d)
>>> foo_dict
{1: <__main__.Foo object at 0x03E01B50>}
>>>
I stumbled upon this SO answer the other day. Using that class decorator/descriptor, you could create a class factory that produces Foo objects and keeps track of the current object and a counter for the next object.
class InnerClassDescriptor(object):
'''allows access to the outer class and its attributes
decorator/descriptor
an instance of a nested inner class can access the outer class and its attributes
'''
def __init__(self, cls):
self.cls = cls
def __get__(self, instance, outerclass):
class Wrapper(self.cls):
outer = instance
Wrapper.__name__ = self.cls.__name__
return Wrapper
class FooFactory(object):
next_foo = 0
this_foo = None
#InnerClassDescriptor
class Foo(object):
def __init__(self):
# print 'Foo,__init__, next_foo = ', self.outer.next_foo
self.name = 'Foo' + str(self.outer.next_foo)
self.outer.next_foo += 1
self.outer.this_foo = self
Usage:
ff = FooFactory()
d = {ff.this_foo.name:ff.Foo()}
for k, v in d.items():
print k, v.name
>>>
Foo0 Foo0
>>>
This relies on the dictionary item value being evaluated before the key - which seems to be the case for Python 2.7
Do it the other way round:
class Foo:
def __init__(self, name=None):
self.name = name
foo1 = Foo('foo1')
foo_dict = {
foo1.name: foo1
}

create objects on runtime in python

how do i create object-instances on runtime in python?
say i have 2 classes:
class MyClassA(object):
def __init__(self, prop):
self.prop = prop
self.name = "CLASS A"
def println(self):
print self.name
class MyClassB(object):
def __init__(self, prop):
self.prop = prop
self.name = "CLASS B"
def println(self):
print self.name
and a dict
{('a': MyClassA), ('b': MyClassB)}
how can i create dynamic an instance of one of my two classes, depending of i choose 'a' or 'b'.
kind of this:
somefunc(str):
if 'a': return new MyClassA
if 'b': return new MyClassB
to get "CLASS B" on calling: somefunc('a').println
but in a more elegant and dynamic way (say i add more classes to the dict on runtime)
You might create a dispatcher, which is a dictionary with your keys mapping to classes.
dispatch = {
"a": MyClassA,
"b": MyClassB,
}
instance = dispatch[which_one]() # Notice the second pair of parens here!
You create a class instance by calling the class. Your class dict {('a': MyClassA), ('b': MyClassB)} returns classes; so you need only call the class:
classes['a']()
But I get the sense you want something more specific. Here's a subclass of dict that, when called with a key, looks up the associated item and calls it:
>>> class ClassMap(dict):
... def __call__(self, key, *args, **kwargs):
... return self.__getitem__(key)(*args, **kwargs)
...
>>> c = ClassMap()
>>> c['a'] = A
>>> c['b'] = B
>>> c('a')
<__main__.A object at 0x1004cc7d0>

Categories