How to define a class with dynamic attributes? - python

In my project, I need to create a class with attributes passed by a dict, something like this:
class_attributes = {"sensor": Nested(Sensor),
"serial_interface": Nested(SerialInterface)}
class MSchema(marshmallow.ModelSchema):
class Meta:
model = cls
attr = class_attributes
I need that "sensor" and "serial_interface" to be in the class, and can be access using MSchema.sensor or MSchema.serial_interface.

You can call the metaclass of ModelSchema directly, rather than defining the class declaratively using a class statement.
m = marshmallow.ModelSchema
class_attributes = {
"sensor": Nested(Sensor),
"serial_interface": Nested(SerialInterface)
}
m = marshmallow.ModelSchema
mc = type(m)
MSchema = mc('MSchema', (m,), {
'Meta': type('Meta', (), {'model': cls}),
**class_attributes
})
In case you aren't aware, a class statement is just a declarative syntax for calling type (or some other metaclass) with 3 arguments: the name of the class, a tuple of parent classes, and a dict of class attributes. The class statement evaluates its body to produce the dict, then calls type (or another given metaclass), and binds the return value to the name. Some simpler examples:
# Foo = type('Foo', (), {})
class Foo:
pass
# Foo = Bar('Foo', (), {})
class Foo(metaclass=Bar):
pass
# Foo = Bar('Foo', (Parent,), {'x': 3})
class Foo(Parent, metaclass=Bar):
x = 3
# def foo_init(self, x):
# self.x = x
# Foo = Bar('Foo', (), {'__init__': foo_init})
class Foo(metaclass=Bar):
def __init__(self, x):
self.x = x

Not entirely sure I understand the question to 100%, but have you tried using setattr()?
Example code would look like the following:
m_schema = MSchema()
for key, value in class_attributes.items():
setattr(m_schema, key, value)
setattr(object, string, value) takes an object to set attributes on, a string for the attribute name, and an arbitrary value as the attribute value.

Related

How to get all values of variables in class?

class NiceClass():
some_value = SomeObject(...)
some_other_value = SomeOtherObject(...)
#classmethod
def get_all_vars(cls):
...
I want get_all_vars() to return [SomeObject(...), SomeOtherObject(...)], or more specifically, the values of the variables in cls.
Solutions tried that didn't work out for me:
return [cls.some_value, cls.some_other_value, ...] (requires listing the variable manually)
subclassing Enum then using list(cls) (requires using some_value.value to access the value elsewhere in the program, also type hinting would be a mess)
namedtuples (nope not touching that subject, heard it was much more complicated than Enum)
[value for key, value in vars(cls).items() if not callable(value) and not key.startswith("__")] (too hacky due to using vars(cls), also for some reason it also includes get_all_vars due to it being a classmethod)
There are two ways. This is a straight answer to your question:
class Foo:
pass
class Bar:
x: int = 1
y: str = 'hello'
z: Foo = Foo()
#classmethod
def get_all(cls):
xs = []
for name, value in vars(cls).items():
if not (name.startswith('__') or isinstance(value, classmethod)):
xs.append(value)
return xs
This is what I suggest:
from dataclasses import dataclass, fields
class Foo:
pass
#dataclass
class Bar:
x: int = 1
y: str = 'hello'
z: Foo = Foo()
#classmethod
def get_defaults(cls):
return [f.default for f in fields(cls)]
#classmethod
def get_all(cls):
return [getattr(cls, f.name) for f in fields(cls)]
results:
Bar.get_defaults() == Bar.get_all()
# True -> [1, 'hello', __main__.Foo]
Bar.x = 10
Bar.get_defaults() == Bar.get_all()
# False -> [1, 'hello', __main__.Foo] != [10, 'hello', __main__.Foo]
You can create a list of values and define individual attributes at the same time with minimal boilerplate.
class NiceClass():
(some_value,
some_other_value,
) = _all_values = [
SomeObject(...),
SomeOtherObject(...)
]
#classmethod
def get_all_vars(cls):
return cls._all_values
The most obvious drawback to this is that it's easy to get the order of names and values out of sync.
Ideally, you might like to do something like
class NiceClass:
_attributes = {
'some_value': SomeObject(...),
'some_other_value': SomeOtherObject(...)
}
#classmethod
def get_all_vars(cls):
return cls._attributes.values()
and have some way to "inject" the contents of _attributes into the class namespace directly. The simplest way to do this is with a mix-in class that defines __init_subclass__:
class AddAttributes:
def __init_subclass__(cls, **kwargs):
super().__init_subclass__(**kwargs)
cls.__dict__.update(cls._attributes)
class NiceClass(AddAttributes):
# As above
...
This might sound like a https://xyproblem.info/ but my solution might work in the other case as well. You can get the fields of an object by using __dict__ or vars (which is considered more pythonic given: Python dictionary from an object's fields)
You could do something like:
class ClassTest:
def __init__(self):
self.one = 1
self.two = 2
list(vars(ClassTest()).values())
But you will see that it has some limitations. It doesn't recognize the not in self.variable_name defined variables like you have used above. It might help you nonetheless, because you can simply define them in init.

convert python objects to string

I have different python classes and I want to convert instances of these classes to ascii strings.Suppose that I have the following objects:
class A:
name = "A"
field = 2
x = "a"
class B:
name = "b"
item = "c"
a = A()
b = B()
I want a function like serialize() which converts the objects as follows:
serialize(a) # results "A2a"
serialize(b) # results "bc"
I don't want to write a serialization function for every classe, I want to have a function capable of serializing all classes. One way is to use the dir() function to get a list of object attributes and then create a string out of them, but the dir() function does not return the attributes in the same order that they are defined. For example calling dir(a) would return ['__doc__', '__module__', 'filed', 'name', 'x'], and I cannot find out which attribute is defined first in the class.
Thanks.
You can override __repr__ function for print Object the desired way:
class A:
name = "A"
field = 2
x = "a"
def __repr__(self):
return A.name + str(A.field) + A.x
You have to create methods in your classes:
ON behalf to put the value in the class the best can be to pass argument of class A during the call%
class A:
def __init__(self, name, field, x) :
self.name = name
self.field = field
self.x = x
def serialize(self) :
return f"{self.name}{self.field}{self.x}"
#main:
a=A(name="a", field="field", x="x")
result=a.serialize()
print(result)

python OOP class method retaining variable. Weird scoping thing

I have two classes, a main class which creates instances of my other class.
class Builder:
def __init__(self, id):
self.id = id
def build_thing(self, main_ftr, main_attr, attrs = {}):
# note the main ftr/attrs gets added to attrs no matter what
attrs[main_ftr] = attrs.get(main_ftr, []) + [main_attr]
return Thing(main_ftr, main_attr, attrs)
class Thing:
def __init__(self, main_ftr, main_attr, attrs):
self.main_ftr = main_ftr
self.main_attr = main_attr
self.attrs = attrs
The issue I'm having has to do with the attrs dictionary that gets passed to the Thing class. The problem is that each time I use the Builder class to create a Thing class, the attrs argument retains it's previous values
b = Builder('123')
t = b.build_thing('name', 'john')
print(t.attrs) # {'name': ['john'] }
# Goal is this creates a new "Thing" with only attrs = {'name':['mike']}
t2 = b.build_thing('name', 'mike')
print(t2.attrs) # {'name': ['john', 'mike']}
My Question is 2 part:
Why is this happening?
How do I fix it?
Functions' optional arguments are initialized once. Since attrs is mutable, each time you call the function, you add new key-value pair to this dictionary and it is kept for further calls. If you need a mutable data structure as a default parameter, use something like:
def build_thing(self, main_ftr, main_attr, attrs=None):
if attrs is None:
attrs = {}
attrs[main_ftr] = attrs.get(main_ftr, []) + [main_attr]
return Thing(main_ftr, main_attr, attrs)

Call constructor of cls object in Python

I am trying to call the constructor of a class object in python. I managed to get it to work using the following few lines:
obj = cls.__new__(cls)
n = (List of attribute names)
v = (List of attribute values)
for s in n:
setattr(obj, s, v[s])
I was wondering if there is a way to directly insert the attribute value + name pairs into the constructor, cause the arguments are just ignored if i call the following:
obj = cls.__new__(cls, v)
p.s.: I am using python3
The class looks similar to this:
class InheritingClass(BaseClass):
def __init__(self, basic_attribute, another_attribute=None):
super().__init__(basic_attribute=basic_attribute)
self.another_attribute= another_attribute
class BaseClass:
def __init__(self, basic_attribute=1):
self.basic_attribute= basic_attribute
So nothing special there
I was wondering if there is a way to directly insert the attribute value + name pairs into the constructor
Please don't do that. This would be the anti pattern. Instead, use the __init__ method to set the values. The __new__ method should be the memory space allocation that returns the object instance, obj in your case.
So you should probable better do this inside your __init__:
k = ['a', 'b', 'c']
v = [1, 2, 3]
d = dict(zip(k, v))
class C:
def __init__(self, d):
for _ in d:
setattr(self, _, d[_])
ci=C(d)
print(ci.a) # 1
I used the dict as __init__ parameter, where I used the zip method to create one.
__init__ is the constructor of Python class instead of __new__. Refer Pythons use of new and init for more information.
To add, if you want to store arbitrary attributes to your class, you can use dict.update like so:
class BaseClass:
def __init__(self, basic_attribute=1, **kw):
self.basic_attribute = basic_attribute
self.__dict__.update(**kw)
class InheritingClass(BaseClass):
def __init__(self, basic_attribute, another_attribute=None, **kw):
super().__init__(basic_attribute=basic_attribute, **kw)
self.another_attribute = another_attribute
Then:
ic = InheritingClass('hi', a=1, b=20)
print(ic.a, ic.b) # prints 1, 20
To answer the question "How do you call the constructor on a class object?" you need to look at the comments from Amadan way back on Aug 24, 2016 at 6:41.
The answer:
new_obj = cls()
Here's some example code that illustrates the point:
class C:
#classmethod
def c(cls):
return cls()
c = C.c()
print(c) # displays <__main__.C object at 0x10ef16a90>
class D(C):
pass
d = D.c()
print(d) # displays <__main__.D object at 0x10ef16370>
And so we see that you can instantiate an object from the cls object.
Now if we combine Amadan's comment with prosti's cool code for setting attributes, we get this:
class ObjectFactory:
#classmethod
def new(cls,**kwargs):
return cls(**kwargs)
def __init__( self, **kwargs ):
for _ in kwargs:
setattr( self, _ , kwargs[ _ ] )
class Person(ObjectFactory):
pass
person = Person.new( first = "John", last = "Doe" )
print(person) # <__main__.Person object at 0x10fe49ff0>
print(person.__dict__) # {'first': 'John', 'last': 'Doe'}

How to inherit a class attribute that is a dict and make it unique?

This works as I expected;
class Foo(object):
name = ''
class Bar1(Foo):
pass
class Bar2(Foo):
pass
Bar1.name == Bar2.name # returns True
Bar1.name = 'Bar1'
Bar1.name == Bar2.name # returns False, which is what I want.
This doesn't work the same, but I want it to;
class Foo(object):
fields = {'name':''}
class Bar1(Foo):
pass
class Bar2(Foo):
pass
Bar1.fields['name'] == Bar2.fields['name'] # returns True
Bar1.fields['name'] = 'Bar1'
Bar1.fields['name'] == Bar2.fields['name'] # returns True, which isn't what I want.
It seems the subclasses are still pointing at the same dict object specified in the main class, but I want them to have unique dicts. How can I do this without writing fields = {'name':''} in each of the subclasses?
PS- I definitely do want to use class level attributes, not instance attributes, as all of the instances I create will use this 'shared' dict.
The simplest way to do this is using a meta-class (I've assumed Python 2.x syntax):
class FieldsMeta(type):
def __new__(mcs, name, bases, dict):
"""This controls the creation of each new class."""
dict.update(fields={'name': ''})
return type.__new__(mcs, name, bases, dict)
class Parent(object):
__metaclass__ = FieldsMeta
class Child(Parent):
pass
In use:
>>> Parent.fields
{'name': ''}
>>> Child.fields
{'name': ''}
>>> Child.fields is Parent.fields
False
>>> Child.fields['name'] = 'Child'
>>> Child.fields
{'name': 'Child'}
>>> Parent.fields
{'name': ''}
See e.g. the data-model documentation for __new__:
[__new__] is also commonly overridden in custom metaclasses in order
to customize class creation.
and the section on customizing class creation.
You can create fields setter, which will copy dict before assign new value
class Foo(object):
fields = {'name':''}
def set_field(self, field, value):
self.fields = dict(self.fields)
self.fields[field] = value
class Bar1(Foo):
pass
class Bar2(Foo):
pass
Bar1.fields['name'] == Bar2.fields['name'] # returns True
# Bar1.fields['name'] = 'Bar1'
Bar1.set_field(Bar1, 'name', 'Bar1')
print(Bar1.fields['name'] == Bar2.fields['name']) # returns False

Categories