Inheriting static dict via dict.update() - python

Subclasses in my application model inherit static attributes (stored as dictionaries) from the superclass, with each subclass using update() to add fields to it's own static field. But this didn't work how I expected it to. Here's the simple version:
In [19]: class A(object):
s = {1:1}
In [20]: class B(A):
s = A.s.update({2:2})
In [21]: class C(B):
s = B.s.update({3:3})
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-21-4eea794593c8> in <module>()
----> 1 class C(B):
2 s = B.s.update({3:3})
3
<ipython-input-21-4eea794593c8> in C()
1 class C(B):
----> 2 s = B.s.update({3:3})
3
AttributeError: 'NoneType' object has no attribute 'update
This DID work, however, when I was concatenating fields to a static list in each subclass. What am I missing?

update doesn't return a dict; it just modifies the receiver in place. For example, after s = A.s.update({2:2}), A.s is modified, and s is None. You can instead write something like
s = dict(B.s, **{3: 3})
to achieve what you want. Note that in python 3, this won't work since keyword arguments are enforced to be strings. You can write a helper function:
def merge(d1, d2):
d = dict(d1)
d.update(d2)
return d
And use s = merge(B.s, {3: 3}) instead.

Related

Python how to create a class that wraps any value

Let's say I have an Entity class:
class Entity(dict):
pass
def save(self):
...
I can wrap a dict object with Entity(dict_obj)
But is it possible to create a class that can wrap any type of objects, eg. int, list etc.
PS I have come up the following work around, it doesn't work on the more complex objects, but seems to work with basic ones, completely unsure if there are any gotchas, might get penalised with efficiency by creating the class every time, please let me know:
class EntityMixin(object):
def save(self):
...
def get_entity(obj):
class Entity(obj.__class__, EntityMixin):
pass
return Entity(obj)
Usage:
>>> a = get_entity(1)
>>> a + 1
2
>>> b = get_entity('b')
>>> b.upper()
'B'
>>> c = get_entity([1,2])
>>> len(c)
2
>>> d = get_entity({'a':1})
>>> d['a']
1
>>> d = get_entity(map(lambda x : x, [1,2]))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/Users/jlin/projects/django-rest-framework-queryset/rest_framework_queryset/entity.py", line 11, in get_entity
return Entity(obj)
TypeError: map() must have at least two arguments.
Improve efficiency:
EntityClsCache = {}
class EntityMixin(object):
def save(self):
...
def _get_entity_cls(obj):
class Entity(obj.__class__, EntityMixin):
pass
return Entity
def get_entity(obj)
cls = None
try:
cls = EntityClsCache[obj.__class__]
except AttributeError:
cls = _get_entity_cls(obj)
EntityClsCache[obj.__class__] = cls
return cls(obj)
The solution you propose looks elegant, but it lacks caching, as in, you'll construct a unique class every time get_entity() is called, even if types are all the same.
Python has metaclasses, which act as class factories. Given that metaclass' methods override these of class, not the instance, we can implement class caching:
class EntityMixin(object):
pass
class CachingFactory(type):
__registry__ = {}
# Instead of declaring an inner class,
# we can also return type("Wrapper", (type_, EntityMixin), {}) right away,
# which, however, looks more obscure
def __makeclass(cls, type_):
class Wrapper(type_, EntityMixin):
pass
return Wrapper
# This is the simplest form of caching; for more realistic and less error-prone example,
# better use a more unique/complex key, for example, tuple of `value`'s ancestors --
# you can obtain them via type(value).__mro__
def __call__(cls, value):
t = type(value)
typename = t.__name__
if typename not in cls.__registry__:
cls.__registry__[typename] = cls.__makeclass(t)
return cls.__registry__[typename](value)
class Factory(object):
__metaclass__ = CachingFactory
This way, Factory(1) performs Factory.__call__(1), which is CachingFactory.__call__(1) (without metaclass, that'd be a constructor call instead, which would result in a class instance -- but we want to make a class first and only then instantiate it).
We can ensure that the objects created by Factory are the instances of the same class, which is crafted specifically for them at the first time:
>>> type(Factory(map(lambda x: x, [1, 2]))) is type(Factory([1]))
True
>>> type(Factory("a")) is type(Factory("abc"))
True

Misspelled fields in a #dataclass Python class

How to make it raise an exception on setting a misspelled fields in a #dataclass-decorated Python class?
I want a practical way to do this. Do I need to write my own decorator instead?
#dataclass
class C(object):
x: int = 1
obj = C()
obj.y = 2 # should raise an exception
One straightforward way (which works with any class) is to define __slots__:
In [1]: from dataclasses import dataclass
In [2]: #dataclass
...: class Foo:
...: __slots__ = 'bar','baz'
...: bar: int
...: baz: int
...:
In [3]: foo = Foo(42, 88)
In [4]: foo.biz = 10
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-4-d52b60444257> in <module>()
----> 1 foo.biz = 10
AttributeError: 'Foo' object has no attribute 'biz'
The purpose of slots is to serve as a small optimization. It allows the instances of the class to use a symbol table instead of a dict as the namespace of the class. It increases the speed of attribute access slightly, and can significantly improve the per-instance memory usage (because the instance doesn't carry around a dict underneath the hood), however, it disallows dynamic attribute setting.
This is actually my favorite feature of __slots__.
Note, you must take care when using inheritance with slots, at least, if you want subclasses to retain the slots behavior.
One way to do so is to mark the dataclass as frozen:
>>> from dataclasses import dataclass
>>> #dataclass(frozen=True)
... class C:
... x: int = 1
...
>>> c = C()
>>> c.y = 1
Traceback (most recent call last):
File "<input>", line 1, in <module>
File "<string>", line 3, in __setattr__
dataclasses.FrozenInstanceError: cannot assign to field 'y'
But note that this makes all existing attributes, like x in this case, read-only as well.
You can provide your own __setattr__() method that only allows assignment to known fields.
Example:
#dataclass
class C:
x: int = 1
def __setattr__(self, k, v):
if k not in self.__annotations__:
raise AttributeError(f'{self.__class__.__name__} dataclass has no field {k}')
super().__setattr__(k, v)
In then fails/works like this:
[ins] In [3]: obj = C()
...: obj.y = 2
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-3-7a568eb098b1> in <module>
1 obj = C()
----> 2 obj.y = 2
<ipython-input-2-d30972f86fbb> in __setattr__(self, k, v)
5 def __setattr__(self, k, v):
6 if k not in self.__annotations__:
----> 7 raise AttributeError(f'{self.__class__.__name__} dataclass has no field {k}')
8 super().__setattr__(k, v)
9
AttributeError: C dataclass has no field y
[ins] In [4]: obj.x = 23
[ins] In [5]: obj.x
Out[5]: 23

Way to mask functions on Python Object

I have a class that inherit from OrderedDict, but I don't know if this is the right way to accomplish what I need.
I would like the class to have the duel method of the javascript '.' notation like obj.<property> and I would also like the users to be able to access the class properties like obj['myproperty'] but I was to hide all the key() and get() functions. The inheritance model is providing good functionality, but it cluttering up the object with additional methods that are not really needed.
Is it possible to get the dictionary behavior without all the other functions coming along?
For this discussion, let's assume my class is this:
from six.moves.urllib import request
import json
class MyClass(OrderedDict):
def __init__(self, url):
super(MyClass, self).__init__(url=url)
self._url = url
self.init()
def init(self):
# call the url and load the json
req = request.Request(self._url)
res = json.loads(request.urlopen(req).read())
for k,v in res.items():
setattr(self, k, v)
self.update(res)
self.__dict__.update(res)
if __name__ == "__main__":
url = "https://sampleserver5.arcgisonline.com/ArcGIS/rest/services?f=json"
props = MyClass(url=url)
props.currentVersion
Is there another way to approach this dilemma?
Thanks
If all you want is x['a'] to work the same way as x.a without any other functionality of dictionaries, then don't inherit from dict or OrderedDict, instead just forward key/indice operations (__getitem__, __setitem__ and __delitem__) to attribute operations:
class MyClass(object):
def __getitem__(self,key):
try: #change the error to a KeyError if the attribute doesn't exist
return getattr(self,key)
except AttributeError:
pass
raise KeyError(key)
def __setitem__(self,key,value):
setattr(self,key,value)
def __delitem__(self,key):
delattr(self,key)
As an added bonus, because these special methods don't check the instance variables for the method name it doesn't break if you use the same names:
x = MyClass()
x['__getitem__'] = 1
print(x.__getitem__) #still works
print(x["__getattr__"]) #still works
The only time it will break is when trying to use __dict__ since that is where the instance variables are actually stored:
>>> x = MyClass()
>>> x.a = 4
>>> x.__dict__ = 1 #stops you right away
Traceback (most recent call last):
File "<pyshell#36>", line 1, in <module>
x.__dict__ = 1
TypeError: __dict__ must be set to a dictionary, not a 'int'
>>> x.__dict__ = {} #this is legal but removes all the previously stored values!
>>> x.a
Traceback (most recent call last):
File "<pyshell#38>", line 1, in <module>
x.a
AttributeError: 'MyClass' object has no attribute 'a'
In addition you can still use the normal dictionary methods by using vars():
x = MyClass()
x.a = 4
x['b'] = 6
for k,v in vars(x).items():
print((k,v))
#output
('b', 6)
('a', 4)
>>> vars(x)
{'b': 6, 'a': 4}

Can Python classes have members that are accessible, but not from an instance of the class?

So I don't come from a computer science background and I am having trouble googling/SO searching on the right terms to answer this question. If I have a Python class with a class variable objects like so:
class MyClass(object):
objects = None
pass
MyClass.objects = 'test'
print MyClass.objects # outputs 'test'
a = MyClass()
print a.objects # also outputs 'test'
both the class and instances of the class will have access to the objects variable. I understand that I can change the instance value like so:
a.objects = 'bar'
print a.objects # outputs 'bar'
print MyClass.objects # outputs 'test'
but is it possible to have a class variable in Python that is accessible to users of the class (i.e. not just from within the class) but not accessible to the instances of that class? I think this is called a private member or static member in other languages?
Python is designed to allow instances of a class to access that class's attributes through the instance.
This only goes one level deep, so you can use a metaclass:
class T(type):
x = 5
class A(object):
__metaclass__ = T
Note that the metaclass syntax is different in Python 3. This works:
>>> A.x
5
>>> A().x
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'A' object has no attribute 'x'
It doesn't prevent you setting the attribute on instances of the class, though; to prevent that you'd have to play with __setattr__ magic method:
class A(object):
x = 1
def __getattribute__(self, name):
if name == 'x':
raise AttributeError
return super(A, self).__getattribute__(name)
def __setattr__(self, name, value):
if name == 'x':
raise AttributeError
return super(A, self).__setattr__(name, value)
def __delattr__(self, name):
if name == 'x':
raise AttributeError
return super(A, self).__delattr__(name)
The simplest way of achieving it is to use a descriptor. Descriptors are the thing meant for giving a higher level of control over attribute access. For example:
class ClassOnly(object):
def __init__(self, name, value):
self.name = name
self.value = value
def __get__(self, inst, cls):
if inst is not None:
msg = 'Cannot access class attribute {} from an instance'.format(self.name)
raise AttributeError(msg)
return self.value
class A(object):
objects = ClassOnly('objects', [])
Used as:
In [11]: a = A()
In [12]: a.objects
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-12-24afc67fd0ba> in <module>()
----> 1 a.objects
<ipython-input-9-db6510cd313b> in __get__(self, inst, cls)
5 def __get__(self, inst, cls):
6 if inst is not None:
----> 7 raise AttributeError('Cannot access class attribute {} from an instance'.format(self.name))
8 return self.value
AttributeError: Cannot access class attribute objects from an instance
In [13]: A.objects
Out[13]: []
If you want there to be a "single source of truth" for objects, you could make it a mutable type:
class MyClass(object):
objects = []
With immutable types, the fact that each instance starts out with the same reference from MyClass is irrelevant, as the first time that attribute is changed for the instance, it becomes "disconnected" from the class's value.
However, if the attribute is mutable, changing it in an instance changes it for the class and all other instances of the class:
>>> MyClass.objects.append(1)
>>> MyClass.objects
[1]
>>> a = MyClass()
>>> a.objects
[1]
>>> a.objects.append(2)
>>> a.objects
[1, 2]
>>> MyClass.objects
[1, 2]
In Python, nothing is really "private", so you can't really prevent the instances from accessing or altering objects (in that case, is it an appropriate class attribute?), but it is conventional to prepend names with an underscore if you don't ordinarily want them to be accessed directly: _objects.
One way to actually protect objects from instance access would be to override __getattribute__:
def __getattribute__(self, name):
if name == "objects":
raise AttributeError("Do not access 'objects' though MyClass instances.")
return super(MyClass, self).__getattribute__(name)
>>> MyClass.objects
[1]
>>> a.objects
...
AttributeError: Do not access 'objects' though MyClass instances.
No, you can't (EDIT: you can't in a way that is completely unaccessible, like in Java or C++).
You can do this, if you like:
class MyClass(object):
objects = None
pass
MyClass_objects = 'test'
print MyClass_objects # outputs 'test'
a = MyClass()
print a.objects # outputs 'None'
or this:
in your_module.py:
objects = 'test'
class MyClass(object):
objects = None
pass
in yourapp.py:
import your_module
print your_module.objects # outputs 'test'
a = your_module.MyClass()
print a.objects # outputs 'None'
the reason is:
When you create an instance of some class there is nothing to prevent
you from poking around inside and using various internal, private
methods that are (a) necessary for the class to function, BUT (b) not
intended for direct use/access.
Nothing is really private in python. No class or class instance can
keep you away from all what's inside (this makes introspection
possible and powerful). Python trusts you. It says "hey, if you want
to go poking around in dark places, I'm gonna trust that you've got a
good reason and you're not making trouble."
Karl Fast

Creating multiple objects within the same class in Python

I want to be able to return multiple objects of a class by using a method within the class. Something like this.
class A:
def __init__(self,a):
self.a = a
def _multiple(self,*l):
obj = []
for i in l:
o = self.__init__(self,i)
obj.append(o)
return obj
When I execute this on iPython (iPython 0.10 and Python 2.6.6) I get the following
In [466]: l = [1,2]
In [502]: A._multiple(*l)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
TypeError: unbound method _multiple() must be called with A instance as
first argument (got int instance instead)
I'm definitely unclear about the invocation as well as the 'self' keyword usage. Could you help me out in getting this correct?
Thank you.
TypeError: unbound method _multiple() must be called with A instance
as first argument (got int instance instead)
The Error is self explanatory. It means that an instance method is being called as a Class method. To make the instance method as a class method add the decorator #classmethod
>>> class A:
def __init__(self,a):
self.a = a
#classmethod
def _multiple(cls,*l):
#Create multiple instances of object `A`
return [A(i) for i in l]
>>> l = [1,2]
>>> A._multiple(*l)
[<__main__.A instance at 0x066FBB20>, <__main__.A instance at 0x03D94580>]
>>>
You want a class method:
class A:
def __init__(self,a):
self.a = a
#classmethod
def _multiple(cls,*l):
obj = []
for i in l:
o = cls(i)
obj.append(o)
return obj
>>> A._multiple(1, 2) # returns 2 A's
[<__main__.A instance at 0x02B7EFA8>, <__main__.A instance at 0x02B7EFD0>]
The classmethod decorator replaces the usual self as the first parameter with a reference to the class (in this case A). Note that doing it this way means if you subclass A and call _multiple on the subclass it will be passed the subclass instead.
class B(A): pass
>>> B._multiple(1, 2, 3)
[<__main__.B instance at 0x02B87C10>, <__main__.B instance at 0x02B87D28>, <__main__.B instance at 0x02B87CD8>]
would create a list of B objects.
Simply replace:
self.__init__(self, i)
With:
A(i)
The reason for this is that the init method mutates the object on which it is called, and "self" is the current instance. You use the constructor (the same name as the class) to create a new instance.

Categories

Resources