Is it possible to create a class that, when instantiated, would never throw AttributeError, but instead would call own method to define the attribute?
Currently, I can do:
class O: pass
o = O()
o.car = O()
o.car.type = "family"
o.car.color = "red"
o.car.trail = O()
o.car.trail.color = "yellow"
o.house = O()
o.house.size = "small"
o.house.number = 87
I'm trying to create an arbitrary hierarchy out of a set of generic objects. Only purpose is to hold arbitrary data i.e. no need to define class Car: or class House:.
Now I wonder if it's possible to define a "magical" method that would be called each time an attribute is not found:
class O:
def __magically_define_attribute__(self, name):
setattr(self, name, O())
so I could omit all the O()s and simply do:
o = O()
o.car.type = "family"
o.car.color = "red"
o.car.trail.color = "yellow"
o.house.size = "small"
o.house.number = 87
So is it possible? And if not, is there a specific reason for that?
(Yes, it's about data, so I know I should probably use hierarchy of dictionaries here, but I still wonder if it's possible since compared to dict syntax, dot syntax is obviously way easier to read and write.)
Do you mean something like this?
class Obj(object):
def __getattr__(self,x):
setattr(self,x,Obj())
return getattr(self,x)
o = Obj()
o.car.type = "family"
o.car.color = "red"
print o.car.color
the __getattr__ method looks like it should fufill your needs. It is invoked when
an attribute lookup has not found the attribute in the usual places
(i.e. it is not an instance attribute nor is it found in the class
tree for self). name is the attribute name
Related
I think a code sample will better speak for itself:
class SomeClass:
example = create_get_method()
Yes, that's all – ideally.
In that case, create_get_method would add a get_example() to SomeClass in a way that it can be accessed via an instance of SomeClass:
obj = SomeClass()
obj.get_example() <- returns the value of self.example
(Of course, the idea is to implement a complex version of get_contact, that's why I want to do that in a non-repetitive way, and this is a simplified version that represents well the issue.)
I don't know if that's possible, because it require to have access to the property name (example) and the class (SomeClass) since these can not be guessed in advance (that function will be used on many and various classes).
I know it's something possible, because that's kind of what SQLAlchemy does with their relationship() function on a class:
class Model(BaseModel):
id = ...
contact_id = db.Integer(db.ForeignKey..)
contact = relationship('contact') <-- This !
How can this be done?
Objects bound to class-level variables can have a __set_name__ method that will be called immediately after the class object has been created. It will be called with two arguments, the class object, and the name of the variable the object is saved as in the class.
You could use this to create your extra getter method, though I'm not sure why exactly you want to (you could make the object a descriptor instead, which would probably be better than adding a separate getter function to the parent class).
class create_get_method:
def __set_name__(self, owner, name):
def getter(self):
return getattr(self, name)
getter_name = f"get_{name}"
getter.__name__ = getter_name
setattr(owner, getter_name, getter)
# you might also want a __get__ method here to give a default value (like None)
Here's how that would work:
>>> class Test:
... example = create_get_method()
...
>>> t = Test()
>>> print(t.get_example())
<__main__.create_get_method at 0x000001E0B4D41400>
>>> t.example = "foo"
>>> print(t.get_example())
foo
You could change the value returned by default (in the first print call), so that the create_get_method object isn't as exposed. Just add a __get__ method to the create_get_method class.
You can do this with a custom non-data descriptor, like a property, except that you don't need a __set__ method:
class ComplicatedDescriptor:
def __init__(self, name):
self.name = name
def __get__(self, owner, type):
# Here, `owner` is the instance of `SomeClass` that contains this descriptor
# Use `owner` to do some complicated stuff, like DB lookup or whatever
name = f'_{self.name}'
# These two lines for demo only
value = owner.__dict__.get(name, 0)
value += 1
setattr(owner, name, value)
return value
Now you can have any number of classes that use this descriptor:
class SomeClass:
example = ComplicatedDescriptor('example')
Now you can do something like:
>>> inst0 = SomeClass()
>>> inst1 = SomeClass()
>>> inst0.example
1
>>> inst1.example
1
>>> inst1.example
2
>>> inst0.example
2
The line name = f'_{self.name} is necessary because the descriptor here is a non-data descriptor: it has no __set__ method, so if you create inst0.__dict__['example'], the lookup will no longer happen: inst0.example will return inst0.__dict__['example'] instead of calling SomeClass.example.__get__(inst0, type(inst0)). One workaround is to store the value under the attribute name _example. The other is to make your descriptor into a data descriptor:
class ComplicatedDescriptor_v2:
def __init__(self, name):
self.name = name
def __get__(self, owner, type):
# Here, `owner` is the instance of `SomeClass` that contains this descriptor
# Use `owner` to do some complicated stuff, like DB lookup or whatever
# These two lines for demo only
value = owner.__dict__.get(self.name, 0)
value += 1
owner.__dict__[self.name] = value
return value
def __set__(self, *args):
raise AttributeError(f'{self.name} is a read-only attribute')
The usage is generally identical:
class SomeClass:
example = ComplicatedDescriptor_v2('example')
Except that now you can't accidentally override your attribute:
>>> inst = SomeClass()
>>> inst.example
1
>>> inst.example
2
>>> inst.example = 0
AttributeError: example is a read-only attribute
Descriptors are a fairly idiomatic way to get and set values in python. They are preferred to getters and setters in almost all cases. The simplest cases are handled by the built-in property. That being said, if you wanted to explicitly have a getter method, I would recommend doing something very similar, but just returning a method instead of calling __get__ directly.
For example:
def __get__(self, owner, type):
def enclosed():
# Use `owner` to do some complicated stuff, like DB lookup or whatever
name = f'_{self.name}'
# These two lines for demo only
value = owner.__dict__.get(name, 0)
value += 1
setattr(owner, name, value)
return value
return enclosed
There is really no point to doing something like this unless you plan on really just want to be able to call inst.example().
Say for example I have some classes which all inherent from the same parent class and have the same parameters. A common example;
class Pet():
...
class Cat(Pet):
__init__(self,name,colour):
Pet.__init__(self,name,colour)
....
class Cactus(Pet):
__init__(self,name,colour):
Pet.__init__(self,name,colour)
....
And then say I want to instantite some type of pet later in the program based on user input. What I would think of doing at first is;
if(pet_type == 'Cat'):
animal = Cat(name,colour)
elif(pet_type == 'Cactus'):
animal = Cactus(name,colour)
etc...
But is there a better way that does not require an if? For example if the program was developed to include over 1000 animals which all descend from pet then it would not be feasilbe.
Create a dictionary of allowable classes:
classes = {
'Cat': Cat,
'Cactus': Cactus,
}
try:
cls = classes[pet_type]
except KeyError:
# handle invalid pet_type here
else:
animal = cls(name, colour)
Depending on your response to a KeyError, you may want to use a finally clause instead of an else clause, or simply use the get method of the dict, e.g.
animal = classes.get(pet_type, Pet)(name, colour)
What you look for is known as factory pattern. There is a great many ways of achieving this, ranging from explicit if-cascades as you show them to meta-class magic.
A rather straightforward way is a class-decorator:
PET_CLASSES = {}
def pet_class(cls):
PET_CLASSES[cls.__name__.lower()] = cls
def create_pet(name):
return PET_CLASSES[name.lower()]()
#pet_class
class Cat:
pass
#pet_class
class Dog:
pass
print(create_pet("dog"))
You can get the class by its string name with getattr()
my_class = getattr(module, "class_name")
and then
my_object = my_class(...)
The module object can be accessed by module = __import__("module_name")
I'm collecting instances using the following code:
class Hand():
instances = []
def __init__(self):
Hand.instances.append(self)
self.value = 5
def do_something(self, a):
self.value = self.value * a
class Foo():
def __init__(self):
pass
def insty(self):
self.hand1 = Hand()
self.hand2 = Hand()
foo = Foo()
foo.insty()
print Hand.instances
for hand in Hand.instances:
print "how do I print the instance name?"
The last line is just a way to learn how to to access the instance name so i can call the 'do_something' method on each instance in order.
How do I access the instance name for each instance of Hand?
If you mean how to get hand1 from the instance you assigned to self.hand1, the answer is that you can't. When you do self.hand1 = Hand(), you tell the Foo object it has a Hand, but the Hand object has no knowledge that it has been assigned to a Foo. You could do this:
h = Hand()
self.bob = h
self.larry = h
Now what is the "name" of that Hand supposed to be? You assigned the same hand to both "bob" and "larry", so there's no way it can have a single unique name.
If you want to have a name for each hand, you need to tell the hand what name you want to give it. You would have to modify your Hand code to allow you to pass a name to the constructor, then create the Hand with Hand("some name").
You can of course give the hands "names" by assigning attributes on them:
self.hand1 = Hand()
self.hand1.name = "hand 1"
. . . but these names are not special or "automatic" in any way.
The bottom line is that if you want something to have a name, you need to decide how to handle that name. You need write your own code that gives it its name, and your own code that retrieves its name.
I don't know if this would solve your problem or not. I needed to get instance names in order to do clear error reporting. Everywhere I looked, folks said "variables don't have names! The name is just a pointer to the thing!"
But it turns out that getting instance names in python is pretty straightforward.
Here's how I did it:
import gc
def instance_names(self):
referrers = gc.get_referrers(self)
result = []
dict_of_things = {}
for item in referrers:
if isinstance(item, dict):
dict_of_things = item
for k, v in dict_of_things.items():
if v == self:
result.append(k)
if not result:
result = ['unnamed instance']
return result
foo = Foo() means that the variable foo just points to the object returned by Foo(), there's no concept of name here.
foo.__dict__ will have "hand1" and "hand2" keys (among others). But you're probably going about this the wrong way. If the names are significant, you should use them as explicit indices in Foo (or somewhere).
e.g.
class Foo():
def __init__(self):
self.hands = {}
def insty(self):
self.hands['hand1'] = Hand()
self.hands['hand2'] = Hand()
I'm trying to create a static variable to be accessed through different classes, assigning value to it, and getting this value when needed. I did use this way in order to achieve this, and that leads me to including a property as following:
class GetPartition(Partition):
_i = 100
def __init__(self):
super(Partition,self).__init__("get")
def get_i(self):
return type(self)._i
def set_i(self,val):
type(self)._i = val
i = property(get_i, set_i)
and this is class Partition if needed:
class Partition(BaseCommand):
def __init__(self,type):
super(Partition,self).__init__("databaseTest")
self.type = type
So, when assigning a value to ifrom another class, I'm assigning it directly like:
GetPartition.i = 5
and among that class when printing GetPartition.i it gives me 5, but when trying to get this value from another class:
partitionNum = GetPartition()
print(partitionNum.i) # 100
print(partitionNum.get_i()) # 100
print(GetPartition.i) # <property object at 0x12A938D0>
print(GetPartition._i) # 100
As I explained in my comment, the problem comes when you assign 5 to i by way of:
GetPartition.i = 5
With this line of code, you are overwriting the property, and "bypassing" the property setter. What I mean by that is: the property setter is not called when you call its attribute name from the class; it is only called when you call its attribute name from a class instance.
Since it has been overwritten, the property no longer exists at that point and all references to the i attribute, whether from class instances or from the class itself, are distinct. They will no longer retrieve the same object, but distinct objects.
You can confirm this problem by doing this:
gp = GetPartition()
print(GetPartition.i) # the property is returned
GetPartition.i = 5 # here the property is overwritten
print(GetPartition.i) # 5 ; the property is gone
print(gp.i) # 5 because gp instance doesn't have its own i
gp.i = 2 # now gp does have its own i
print(gp.i) # 2
print(GetPartition.i) # 5 ; i is not synced
As I said above, the property getters and setters (and descriptors in general) only work with instances of GetPartition, not the class itself. They can be forced to work with the class itself by creating a metaclass - which is the class of a class - for your class; this is considered "deep black magic" by many people, and I don't recommend going that route if you can avoid it.
I believe the below example is probably the simplest way to implement the behavior you want. This approach abandons the use of properties in favor of overriding the attribute getter and setter methods directly:
class Example():
i = 1 # this is a "static variable"
j = 3 # this is a regular class attribute
#designate which of the class attributes are "static"
statics = {'i'}
def __getattribute__(self, attr):
'''Overrides default attribute retrieval behavior.'''
if attr in Example.statics:
#use class version if attr is a static var
return getattr(Example, attr)
else:
#default behavior if attr is not static var
return super().__getattribute__(attr)
def __setattr__(self, attr, value):
'''Overrides default attribute setting behavior.'''
if attr in Example.statics:
#use class version if attr is a static var
setattr(Example, attr, value)
else:
#default behavior if attr is not static var
super().__setattr__(attr, value)
#testing
if __name__ == '__main__':
print("\n\nBEGIN TESTING\n\n")
e = Example()
#confirm instance and class versions of i are the same
test = "assert e.i is Example.i"
exec(test)
print(test)
e.i = 5
#confirm they remain the same after instance change
test = "assert e.i is Example.i"
exec(test)
print(test)
Example.i = 100
#confirm they remain the same after class change
test = "assert e.i is Example.i"
exec(test)
print(test)
e.j = 12
#confirm both versions of j are distinct
test = "assert e.j is not Example.j"
exec(test)
print(test)
print("\n\nTESTING COMPLETE\n\n")
If you are not familiar with __getattribute__ and __setattr__, I should let you know that overriding them is often quite perilous and can cause big problems (especially __getattribute__). You'll find many people simply say "don't do it; rethink your problem and find another solution". Doing the overrides correctly requires a deep understanding of a wide range python topics.
I do not claim to have this deep understanding (though I think I have a pretty good understanding), so I cannot be 100% certain that my overrides as given above will not lead to some other problem for you. I believe they are sound, but just be aware that these particular corners of python can be pretty tricky.
I'm collecting instances using the following code:
class Hand():
instances = []
def __init__(self):
Hand.instances.append(self)
self.value = 5
def do_something(self, a):
self.value = self.value * a
class Foo():
def __init__(self):
pass
def insty(self):
self.hand1 = Hand()
self.hand2 = Hand()
foo = Foo()
foo.insty()
print Hand.instances
for hand in Hand.instances:
print "how do I print the instance name?"
The last line is just a way to learn how to to access the instance name so i can call the 'do_something' method on each instance in order.
How do I access the instance name for each instance of Hand?
If you mean how to get hand1 from the instance you assigned to self.hand1, the answer is that you can't. When you do self.hand1 = Hand(), you tell the Foo object it has a Hand, but the Hand object has no knowledge that it has been assigned to a Foo. You could do this:
h = Hand()
self.bob = h
self.larry = h
Now what is the "name" of that Hand supposed to be? You assigned the same hand to both "bob" and "larry", so there's no way it can have a single unique name.
If you want to have a name for each hand, you need to tell the hand what name you want to give it. You would have to modify your Hand code to allow you to pass a name to the constructor, then create the Hand with Hand("some name").
You can of course give the hands "names" by assigning attributes on them:
self.hand1 = Hand()
self.hand1.name = "hand 1"
. . . but these names are not special or "automatic" in any way.
The bottom line is that if you want something to have a name, you need to decide how to handle that name. You need write your own code that gives it its name, and your own code that retrieves its name.
I don't know if this would solve your problem or not. I needed to get instance names in order to do clear error reporting. Everywhere I looked, folks said "variables don't have names! The name is just a pointer to the thing!"
But it turns out that getting instance names in python is pretty straightforward.
Here's how I did it:
import gc
def instance_names(self):
referrers = gc.get_referrers(self)
result = []
dict_of_things = {}
for item in referrers:
if isinstance(item, dict):
dict_of_things = item
for k, v in dict_of_things.items():
if v == self:
result.append(k)
if not result:
result = ['unnamed instance']
return result
foo = Foo() means that the variable foo just points to the object returned by Foo(), there's no concept of name here.
foo.__dict__ will have "hand1" and "hand2" keys (among others). But you're probably going about this the wrong way. If the names are significant, you should use them as explicit indices in Foo (or somewhere).
e.g.
class Foo():
def __init__(self):
self.hands = {}
def insty(self):
self.hands['hand1'] = Hand()
self.hands['hand2'] = Hand()