How to add attribute to arbitrary object python? - python

For a project I'm working on, I want to be able to associate a name with an object. The way I would like to do it is to set the .name attribute of the object to the name I want. What I really need is a function that takes an instance of an object, and returns something that is identical in every way but with a .name attribute. The problem is that I don't know what type of data the object will be ahead of time, so I can't use subclassing for example
Every method I've tried has hit a problem. Trying to give it a .name attribute directly doesnt work, for example:
>>> cats = ['tabby', 'siamese']
>>> cats.name = 'cats'
Traceback (most recent call last):
File "<pyshell#197>", line 1, in <module>
cats.name = 'cats'
AttributeError: 'list' object has no attribute 'name'
Using setattr has the same problem.
I've tried creating a new class that on init copies all attributes from the instance and also has a .name attribute, but this doesn't work either. If I try:
class NamedThing:
def __init__(self, name, thing):
thing_dict = {#not all types have a .__dict__ method
name: getattr(thing, name) for name in dir(thing)
}
self.__dict__ = thing_dict
self.name = name
It copies over the dict without a problem, but for some reason unless I directly call the new methods, python fails to find them, so the object loses all of its functionality. For example:
>>> cats = ['tabby', 'siamese']
>>> named_thing_cats = NamedThing('cats', cats)
>>> named_thing_cats.__repr__()#directly calling .__repr__()
"['tabby', 'siamese']"
>>> repr(named_thing_cats)#for some reason python does not call the new repr method
'<__main__.NamedThing object at 0x0000022814C1A670>'
>>> hasattr(named_thing_cats, '__iter__')
True
>>> for cat in named_thing_cats:
print(cat)
Traceback (most recent call last):
File "<pyshell#215>", line 1, in <module>
for cat in named_thing_cats:
TypeError: 'NamedThing' object is not iterable
I've also tried setting the type and attributes by setting class directly:
class NamedThing:
def __init__(self, name, thing):
thing_dict = {#not all types have a .__dict__ method
name: getattr(thing, name) for name in dir(thing)
}
self.__class__ = type('NamedThing', (type(thing),), thing_dict)
self.name = name
But this runs into a problem depending on what type thing is:
>>> cats = ['tabby', 'siamese']
>>> named_thing_cats = NamedThing('cats', cats)
Traceback (most recent call last):
File "<pyshell#217>", line 1, in <module>
named_thing_cats = NamedThing('cats', cats)
File "C:/Users/61490/Documents/Python/HeirachicalDict/moduleanalyser.py", line 12, in __init__
self.__class__ = type('NamedThing', (type(thing),), thing_dict)
TypeError: __class__ assignment: 'NamedThing' object layout differs from 'NamedThing'
I'm really stuck, help would be great

What you want is called an object proxy. This is some pretty sophisticated stuff, as you're getting into the data model of python and manipulating some pretty fundamental dunder (double underscore) methods in interesting ways
class Proxy:
def __init__(self, proxied):
object.__setattr__(self, '_proxied', proxied)
def __getattribute__(self, name):
try:
return object.__getattribute__(self, name)
except AttributeError:
p = object.__getattribute__(self, '_proxied')
return getattr(p, name)
def __setattr__(self, name, value):
p = object.__getattribute__(self, '_proxied')
if hasattr(p, name):
setattr(p, name, value)
else:
setattr(self, name, value)
def __getitem__(self, key):
p = object.__getattribute__(self, '_proxied')
return p[key]
def __setitem__(self, key, value):
p = object.__getattribute__(self, '_proxied')
p[key] = value
def __delitem__(self, key):
p = object.__getattribute__(self, '_proxied')
del p[key]
The most obvious thing that's going on here is that internally this class has to use the object implementation of the dunders to avoid recursing infinitely. What this does is holds a reference to a proxied object, then if you try to get or set an attribute it will check the proxied object, if the proxied object has that attribute it uses it, otherwise it sets the attribute on itself. For indexing, like with a list, it just directly acts on the proxied object, since the Proxy itself doesn't allow indexing.
If you need to use this in production, there's a package called wrapt you should probably look at instead.

Why not just create a __iter__ magic method with yield from:
class NamedThing():
def __init__(self, name, thing):
self.thing = thing
self.name = name
def __iter__(self):
yield from self.thing
cats = ['tabby', 'siamese']
named_thing_cats = NamedThing('cats', cats)
for cat in named_thing_cats:
print(cat)
Output;
tabby
siamese

Does this work?
class Thingy(list):
def __init__(self, name, thing):
list.__init__(self, thing)
self.name = name
cats = Thingy('cats', ['tabby', 'siamese'])
print(cats.name) # shows 'cats'
for cat in cats:
print(cat) # shows tabby, siamese
Or you could do:
class Thingy:
def __init__(self, name, thing):
self.thing = thing
self.name = name

Related

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

Accessing an object's attribute inside __setattr__

Python bit me today. I'm trying to access an object's attribute inside its __setattr__ implementation - I can't figure out how. This is what I've tried so far:
class Test1(object):
def __init__(self):
self.blub = 'hi1'
def __setattr__(self, name, value):
print self.blub
class Test2(object):
def __init__(self):
self.blub = 'hi2'
def __setattr__(self, name, value):
print object.__getattr__(self, 'blub')
class Test3(object):
def __init__(self):
self.blub = 'hi3'
def __setattr__(self, name, value):
print object.__getattribute__(self, 'blub')
class Test4(object):
def __init__(self):
self.blub = 'hi4'
def __setattr__(self, name, value):
print self.__getattr__('blub')
class Test5(object):
def __init__(self):
self.blub = 'hi5'
def __setattr__(self, name, value):
print self.__getattribute__('blub')
class Test6(object):
def __init__(self):
self.blub = 'hi6'
def __setattr__(self, name, value):
print self.__dict__['blub']
Testing:
try:
TestX().bip = 'bap'
except Exception as ex:
print ex
with X from 1 to 6
Output:
'Test1' object has no attribute 'blub'
type object 'object' has no attribute '__getattr__'
'Test3' object has no attribute 'blub'
'Test4' object has no attribute '__getattr__'
'Test5' object has no attribute 'blub'
'blub'
Any suggestions?
Because inside the __init__ it is trying to set blub which calls __setattr__; and it does not set anything but tries to access (and print) blub, finds nothing and raises the error. Check this:
>>> class Test2(object):
def __init__(self):
print "__init__ called"
self.blub = 'hi2'
print "blub was set"
def __setattr__(self, name, value):
print "__setattr__ called"
print self.blub
>>> Test2()
__init__ called
__setattr__ called
Traceback (most recent call last):
File "<pyshell#10>", line 1, in <module>
Test2()
File "<pyshell#9>", line 4, in __init__
self.blub = 'hi2'
File "<pyshell#9>", line 9, in __setattr__
print self.blub
AttributeError: 'Test2' object has no attribute 'blub'
>>>
OP, you haven't told us the whole story. You did not just run code like this:
TestX().bip = 'bap'
You ran code like this:
try:
TestX().bip = 'bap'
except Exception as ex:
print ex
There's a big difference. Why, you ask? Well, your output seems on first glance to indicate that Test6 works, and several comments and answers assumed that it did. Why does it appear to work? Reading the code, there's no way it should work. A closer inspection of the source code reveals that if it had worked, it should have printed hi6, not 'blub'.
I put a breakpoint at the print ex line in pdb to examine the exception:
(Pdb) ex
KeyError('blub',)
(Pdb) print ex
'blub'
For some reason print ex does not print KeyError: blub like you'd expect, but just 'blub', which was why Test6 appeared to work.
So we've cleared that up. In the future, please do not leave out code like this because it might be important.
All the other answers correctly point out that you have not set the attribute you're attempting to print, and that this is your problem. The answer you had accepted previously, before you accepted this answer istead, prescribed the following solution:
def __setattr__(self, name, value):
self.__dict__[name] = value
print self.__dict__[name]
While this solution does indeed work, it is not good design. This is because you might want to change the base class at some point, and that base class might might have important side effects when setting and/or getting attributes, or it might not store the attributes in self.__dict__ at all! It is better design to avoid messing around with __dict__.
The correct solution is to invoke the parent __setattr__ method, and this was suggested by at least one other answer, though with the wrong syntax for python 2.x. Here's how I'd do it:
def __setattr__(self, name, value):
super(Test6, self).__setattr__(name, value)
print getattr(self, name)
As you see I'm using getattr instead of __dict__ to look up attributes dynamically. Unlike using __dict__ directly, this will call self.__getattr__ or self.__getattribute__, as appropriate.
__setattr__ works on the class, so when you're over-riding - you need to make it actually set the attribute... otherwise, it'll never exist... eg:
object.__setattr__(self, name, value)
So, in your __init__, when you do self.blub = 'hi' that's in effect a no-op.
You never actually set the attribute in your __setattr__ so of course the object doesn't have it.
def __setattr__(self, name, value):
self.name = value
# this doesn't work, since it calls itself (the __setattr__)
def __setattr__(self, name, value):
super().__setattr__(name, value)
# call the default implementation directly in Py 3.x
def __setattr__(self, name, value):
super(TestX, self).__setattr__(name, value) # for Python 2.x
Of course doing this alone is good for nothing, you usually want to add some functionality oround this like some condition, debug printing, caching or whatever you need.
print self.__dict__['blub']
Prints out blub which is correct. You have to set the new value first, because python won't do that for you, like that:
def __setattr__(self, name, value):
self.__dict__[name] = value
print self.__dict__[name]
Then test6.blub = 'test' should print out test
Edit:
As suggested by #Lauritz you can also use
def __setattr__(self, name, value):
super(Test6, self).__setattr__(name, value)
print self.__dict__[name]
Which invokes the parent function, so if you superclass has already a __setattr__ function it won't get overridden.

Python class which instances' atributes can't be edited

What would be the most convenient way to create a class which instances' attributes can't be changed from outside the class (you could still get the value), so it'd be possible to call self.var = v inside the class' methods, but not ClassObject().var = v outside of the class?
I've tried messing with __setattr__() but if I override it, the name attribute cannot be initiated in the __init__() method. Only way would be to override __setattr__() and use object.__setattr__(), which I am doing at the moment:
class MyClass(object):
def __init__(self, name):
object.__setattr__(self, "name", name)
def my_method(self):
object.__setattr__(self, "name", self.name + "+")
def __setattr__(self, attr, value):
raise Exception("Usage restricted")
Now this solution works, and it's enough, but I was wondering if there's even a better solution. The problem with this one is: I can still call object.__setattr__(MyClass("foo"), "name", "foo_name") from anywhere outside the class.
Is there any way to totally prevent setting the variable to anything from outside of the class?
EDIT: Stupid me not mentioning I'm not looking for property here, some of you already answered it, however it's not enough for me since it will leave self._name changeable.
No, you cannot do this in pure python.
You can use properties to mark your attributes as read-only though; using underscore-prefixed 'private' attributes instead:
class Foo(object):
def __init__(self, value):
self._spam = value
#property
def spam(self):
return self._spam
The above code only specifies a getter for the property; Python will not let you set a value for Foo().spam now:
>>> class Foo(object):
... def __init__(self, value):
... self._spam = value
... #property
... def spam(self):
... return self._spam
...
>>> f = Foo('eggs')
>>> f.spam
'eggs'
>>> f.spam = 'ham'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: can't set attribute
Of course, you can still access the 'private' _spam attribute from outside:
>>> f._spam
'eggs'
>>> f._spam = 'ham'
>>> f.spam
'ham'
You could use the double underscore convention, where attribute names with __ at the start (but not at the end!) are renamed on compilation. This is not meant for making a attribute inaccessible from the outside, it's intent is to protect an attribute from being overwritten by a subclass instead.
class Foo(object):
def __init__(self, value):
self.__spam = value
#property
def spam(self):
return self.__spam
You can still access those attributes:
>>> f = Foo('eggs')
>>> f.spam
'eggs'
>>> f.__spam
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Foo' object has no attribute '__spam'
>>> f._Foo__spam
'eggs'
>>> f._Foo__spam = 'ham'
>>> f.spam
'ham'
There is no strict way of doing encapsulation on Python. The best you can do is prepend 2 underscores __ to the intended to be private attributes. This will cause them to be mangled with the class name (_ClassName_AttribName), so if you try to use them on an inherited class, the base member won't be referenced. The names are not mangled if you use getattrib() or setattrib() though.
You can also override __dir()__ in order to hide them.
You can use properties to simulate such a behavior but like Martijn said, it'll be possible to access the variable directly.
Doing this is a signal of you not understanding Python philosophy, check this out.
Why Python is not full object-oriented?
The properties way:
http://docs.python.org/2/library/functions.html#property
class C(object):
def __init__(self):
self._x = None
def getx(self):
return self._x
def setx(self, value):
raise Exception("Usage restricted")
x = property(getx, setx)

counter part of __getattr__

I am trying to find a way to set dict values encapsulated into a class, for example using __getattr__ i can return the internal dict value, however the __setattr__ is called even when attributes exists, making my implementation ugly. The example below is simplified my actual class inherits from a Subject class (the subject part of the observer pattern)
i am trying to achieve something like this:
obj = Example()
obj.username = 'spidername' # all OK username is a key in the internal dict
# but company is not a key in the internal dict so
obj.company = 'ABC' # will raise AttributeError
and i am asking if there is a better way than the way i am doing below:
class Example(object):
def __init__(self, table=None):
self._fields = {}
self._table = table
def _set_fields(self):
"""
this method will be implemented by
subclasses and used to set fields names and values
i.e.
self._field['username'] = Field(default='unknown', is_primary=False)
"""
raise NotImplementedError
def __getattr__(self, name):
"""
great this method is only called when "name"
is not an attribute of this class
"""
if name in self._fields:
return self._fields[name].value
return None
def __setattr__(self, name, value):
"""
not so great, this method is called even for
attributes that exists in this class
is there a better way to do the following?
this can be in __init__, but its still ugly
"""
attribs = ['_fields', '_table']
if name in attribs:
super(Example, self).__setattr__(name, value)
else:
if name in self._fields:
self._fields[name].value = value
else:
raise AttributeError
EDIT: adjusted comment in code, added missin quotes
The problem is that the attributes don't exist when they are first assigned. In __init__, when you first assign a dict to _fields, _fields is not an attribute. It only becomes an existing attribute after its been assigned. You could use __slots__ if you know in advance what the attributes are, but my guess is that you don't. So my suggestion would be to insert these into the instance dict manually:
class Example(object):
def __init__(self, table=None):
self.__dict__['_fields'] = {}
self.__dict__['_table'] = table
...
def __setattr__(self, name, value):
if name in self._fields:
self._fields[name].value = value
else:
raise AttributeError
However, with this implementation, the only way you can add or change instance attributes later would be through __dict__. But I assume this is not likely.
FWIW, your overall goal can be achieved directly just by using __slots__:
>>> class Example(object):
__slots__ = ['username']
>>> obj = Example()
>>> obj.username = 'spiderman'
>>> obj.company = 'ABC'
Traceback (most recent call last):
File "<pyshell#18>", line 1, in <module>
obj.company = 'ABC'
AttributeError: 'Example' object has no attribute 'company'

__setattr__ only for names not found in the object's attributes`?

I want to use __setattr__ only when the attribute was not found in the object's attributes, like __getattr__.
Do I really have to use try-except?
def __setattr__(self, name, value):
try:
setattr(super(Clazz, self), name, value)
except AttributeError:
# implement *my* __setattr__
pass
You can use hasattr():
def __setattr__(self, name, value):
if hasattr(super(Clazz, self), name):
setattr(super(Clazz, self), name, value)
else:
# implement *my* __setattr__
pass
There are many times when calling hasattr won't work the way you expect (e.g., you've overridden __getattr__ to always return a value), so another way to set the right attribute in the right place would be something like this:
def __setattr__(self, k, v):
if k in self.__dict__ or k in self.__class__.__dict__:
super(Clazz, self).__setattr__(k, v)
else:
# implement *my* __setattr__
pass
__setattr__, if it exists, is called for every attribute set on the object.
Your example code, though, is rather confusing for me. What are you trying to do with the statement:
setattr(super(Clazz, self), name, value) ??
Set an attribute on self, with self viewed as an instance of its superclass? That makes no sense, because the object is still "self".
On the other hand trying to use "setattr" on the object returned by a call to "super" will always yield an attribute error, regardless if the attribute exists on the superclass or not. That is because super returns not the superclass itself, but a wrapper object that wil fetch attributes there when they are needed - so you can use "hasattr" in the object returned by super, but not setattr. I thought it would behave so, and just tried it on the console :
>>> class A(object):pass
...
>>> class B(A): pass
...
>>> b = B()
>>> super(B,b)
<super: <class 'B'>, <B object>>
>>> setattr(super(B,b), "a", 5)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'super' object has no attribute 'a'
>>> A.a = 1
>>> setattr(super(B,b), "a", 5)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'super' object has no attribute 'a'
But then, you can just use "hasattr" in the object itself, and proceed like this:
def __setattr__(self, attr, value):
if hasattr(self, value):
#this works because retrieving "__setattr__" from the
# result of the supercall gives the correct "__setattr__" of the superclass.
super(Clazz, self).__setattr__(self, attr, value)
else:
# transform value /or attribute as desired in your code
super(Clazz, self).__setattr__(self, attr, value)

Categories

Resources