Given a class, how can an instance of it be created from a dictionary of fields? Here is an example to illustrate my question:
from typing import Tuple, Mapping, Any
def new_instance(of: type, with_fields: Mapping[str, Any]):
"""How to implement this?"""
return ...
class A:
"""Example class"""
def __init__(self, pair: Tuple[int, int]):
self.first = pair[0]
self.second = pair[1]
def sum(self):
return self.first + self.second
# Example use of new_instance
a_instance = new_instance(
of=A,
with_fields={'first': 1, 'second': 2}
)
See How to create a class instance without calling initializer? to bypass the initializer. Then set the attributes from the dictionary.
def new_instance(of: type, with_fields: Mapping[str, Any]):
obj = of.__new__(of)
for attr, value in with_fields.items():
setattr(obj, attr, value)
return obj
Related
I can't set the right properties of an instance when setting their attributes via setattr in a factory method.
Given the following code where data is a simple dict containing e.g. { "age": "64", ...}
def factory(data):
obj = MyClass()
for k, v in data.items():
setattr(obj, k, v)
return obj
class MyClass(object):
def __init__(self):
self._age = None
# more...
#property
def age(self):
return self._age
#age.setter
def age(self, value):
some_validation(value)
self._age = value
def __setattr__(self, name, value):
object.__setattr__(self, name, value)
def __getitem__(self, item):
return self.__dict__.get(item, None)
def __getattr__(self, item):
self.__dict__[item] = None
return None
def __str__(self):
return json.dumps(self, default=lambda o: o.__dict__)
c = factory(data)
print(c)
I always get the following output when printing the created object:
{"_age": "64", ...}
But I need to have
{"age": "64", ...}
Why does the setattr method assign the leading underscore?
Some of the things you are trying to achieve get mixed up, like wanting to print __dict__ for a readable representation, but using private attributes for properties. Let's start from scratch and see how we can implement your class correctly.
You are trying to implement a class which attributes can be accessed both as keys and attributes. That is fine and can be accomplished in a more concise way.
class MyClass:
...
def __getitem__(self, item):
return self.__getattribute__(item)
def __setitem__(self, key, value):
return self.__setattr__(key, value)
You also want None to be returned when an attribute does not exist. This is covered by __getattr__ which is called exactly when an attribute does not exist.
def __getattr__(self, _):
return None
Then you want to add some validation to some attributes with property. It is indeed the correct way to proceed.
#property
def age(self):
return self._age
#age.setter
def age(self, value):
# some validation here
self._age = value
And finally you want to be able to have a nice string representation of your instance. We have to be careful for that since we had to add some private attributes that we do not want to print.
What we are going to do is implement a method keys to allow casting to dict. This method will only return keys for attributes which are not private nor methods.
def keys(self):
return [k for k in dir(self) if not k.startswith('_') and not callable(self[k])]
def __str__(self):
return json.dumps(dict(self))
This does the right thing.
obj = MyClass()
obj.age = 3
print(obj)
# prints: {"age": 3}
I am playing a little bit around with Python metaprogramming.
class FormMetaClass(type):
def __new__(cls, clsname, bases, methods):
# Attach attribute names to the descriptors
for key, value in methods.items():
if isinstance(value, FieldDescriptor):
value.name = key
return type.__new__(cls, clsname, bases, methods)
class Form(metaclass=FormMetaClass):
#classmethod
def from_json(cls, incoming):
instance = cls()
data = json.loads(incoming)
for k, v in data.items():
if (not hasattr(instance, k)):
raise KeyError("Atrribute not found")
instance.__setattr__(k, v)
return cls
class MyForm(Form):
first_name = String()
last_name = String()
age = Integer()
def __repr__(self):
return "{} {}".format(self.first_name, self.last_name)
def main():
data = json.dumps({'first_name': 'Thomas',
'last_name': 'Junk'})
form = MyForm.from_json(data)
print(form)
if __name__ == "__main__":
main()
class FieldDescriptor:
def __init__(self, name=None, **opts):
self.name = name
for key, value in opts.items():
setattr(self, key, value)
def __set__(self, instance, value):
instance.__dict__[self.name] = value
class Typechecked(FieldDescriptor):
expected_type = type(None)
def __set__(self, instance, value):
if not isinstance(value, self.expected_type):
raise TypeError('expected ' + str(self.expected_type))
super().__set__(instance, value)
class Integer(Typechecked):
expected_type = int
class String(Typechecked):
expected_type = str
I have a Form which has a metaclass FormMetaClass.
To have an alternative constructor I am using a #classmethod.
I create an instance, which seem to work so far.
What doesn't work is calling the __repr__ (or __str__ interchangeably).
When I create an instance via MyForm() everything is fine.
When I create an instance via the #classmethod, some "default" implementation is taken.
I expected Thomas Junk, but I get <class '__main__.MyForm'>
Could you give me a hint, what I am overlooking?
You are returning the class, not the newly created instance:
return cls
So you return MyForm, not the new instance MyForm() you just set all the attributes on. And you indeed see the repr() output for the class:
>>> form is MyForm
True
>>> print(MyForm)
<class '__main__.MyForm'>
The fix is simple, return instance instead:
return instance
or, as a full method:
#classmethod
def from_json(cls, incoming):
instance = cls()
data = json.loads(incoming)
for k, v in data.items():
if (not hasattr(instance, k)):
raise KeyError("Atrribute not found")
instance.__setattr__(k, v)
return instance
at which point the method returns an instance and everything works:
>>> isinstance(form, MyForm)
True
>>> print(form)
Thomas Junk
In my code, I have a data store with multiple variables set to instances of a class similar to that below. (The reason is that this Interval class has lots of operator overriding functions).
class Interval(object):
def __init__(self, value):
self.value = value
data_store.a = Interval(1)
I want any references to data_store.a to return self.value rather than the Interval instance. Is this possible?
As an alternative to Malik's answer, you could make a a #property, the Pythonic equivalent of get and set for managing access to internal attributes:
class DataStore(object):
def __init__(self):
self.a = Interval(1)
#property
def a(self):
return self._a.value
#a.setter
def a(self, value):
self._a = value
Here _a is a private-by-convention attribute that stores the Interval instance. This works as you want it:
>>> store = DataStore()
>>> store.a
1
You need to extend your data store whose attribute is an interval object:
class DataStore(object):
def __init__(self):
self.a = Interval(1)
def __getattribute__(self, attr):
if attr == 'a':
return object.__getattribute__(self, 'a').value
if attr != 'a':
return object.__getattribute__(self, attr)
Consider a registry with a dict-like interface. Each key is a string name and each value is a class. Using it in this order works:
registry['foo'] = FooClass
cls = registry['foo']
instance = cls
But in this order it wouldn't of course:
cls = registry['foo']
registry['foo'] = FooClass
instance = cls()
To support the second use-case, I implemented a class constructor wrapper in a function but it "denaturates" the class. I mean that this won't work:
cls = registry['foo']
registry['foo'] = FooClass
issubclass(cls, FooClass)
I'd like to support that third case, so I'm looking for a better way to proxy the class registry items.
Interesting problem, I would try something like this:
from abc import ABCMeta
class Registry(object):
def __init__(self):
self._proxies = {}
self._classes = {}
def resolve(self, name):
try:
return self._classes[name]
except KeyError:
raise KeyError('Cannot resolve "%s".'
' Class not registered yet.' % name)
def __getitem__(self, name):
"""Return a proxy class bound to `name`."""
if name not in self._proxies:
self._proxies[name] = make_proxy(lambda: self.resolve(name))
return self._proxies[name]
def __setitem__(self, name, val):
"""Store a class for `name`."""
self._classes[name] = val
def make_proxy(resolve):
"""
Return a proxy class.
:param resolve: a function that returns the actual class
"""
class ProxyMeta(ABCMeta):
"""
Custom meta class based on ABCMeta that forwards various checks
to the resolved class.
"""
def __eq__(self, y):
return resolve() == y
def __repr__(self):
return repr(resolve())
def __str__(self):
return str(resolve())
class Proxy(object):
"""
The actual proxy class.
"""
__metaclass__ = ProxyMeta
def __new__(cls, *args, **kwargs):
"""Calling this class returns an instance of the resolved class."""
return resolve()(*args, **kwargs)
#classmethod
def __subclasshook__(cls, subclass):
"""issubclass() overwrite."""
return issubclass(resolve(), subclass)
return Proxy
>>> registry = Registry()
>>> List = registry['list']
>>> List
KeyError: 'Cannot resolve "list". Class not registered yet.'
>>> registry['list'] = list
>>> List
<type 'list'>
>>> issubclass(List, List)
True
>>> issubclass(list, List)
True
>>> List == list
True
>>> List()
[]
>>> registry['list'] = tuple
>>> List()
()
New to python...
I have the following class Key, that extends dict:
class Key( dict ):
def __init__( self ):
self = { some dictionary stuff... }
def __getstate__(self):
state = self.__dict__.copy()
return state
def __setstate__(self, state):
self.__dict__.update( state )
I want to save an instance of the class with its data using pickle.dump and then retrieve the data using pickle.load. I understand that I am supposed to somehow change the getstate and the setstate, however, am not entirely clear on how I am supposed to do that... any help would be greatly appreciated!
I wrote a subclass of dict that does this here it is.
class AttrDict(dict):
"""A dictionary with attribute-style access. It maps attribute access to
the real dictionary. """
def __init__(self, *args, **kwargs):
dict.__init__(self, *args, **kwargs)
def __getstate__(self):
return self.__dict__.items()
def __setstate__(self, items):
for key, val in items:
self.__dict__[key] = val
def __repr__(self):
return "%s(%s)" % (self.__class__.__name__, dict.__repr__(self))
def __setitem__(self, key, value):
return super(AttrDict, self).__setitem__(key, value)
def __getitem__(self, name):
return super(AttrDict, self).__getitem__(name)
def __delitem__(self, name):
return super(AttrDict, self).__delitem__(name)
__getattr__ = __getitem__
__setattr__ = __setitem__
def copy(self):
return AttrDict(self)
It basically converts the state to a basic tuple, and takes that back again to unpickle.
But be aware that you have to have to original source file available to unpickle. The pickling does not actually save the class itself, only the instance state. Python will need the original class definition to re-create from.