python - setting property of class on module loading - python

I'm working on a code dealing with dict data on python.
While implementing such class, I have to define a lot of properties. It's not that hard, but recently I thought it would be much better if I could use something like helper function.
For example, let's assume that I have a class like the following.
class MyClass(object):
def __init__(self, data):
self.data = data
#property
def version(self):
return self.data["version"]
If I could write this class in something like the following.
class MyClass(object):
def __init__(self, data):
self.data = data
define_own_property("data", "version")
It looks trivial, but if I can do that, I think I can reuse a lot of validation/exception handling cases.
Any idea? :D

You can achieve something like that by just writing a function to return the accessor you want:
def define_own_property(attr, key):
def prop(self):
return getattr(self, attr)[key]
return property(prop)
class MyClass(object):
def __init__(self, data):
self.data = data
version = define_own_property("data", "version")
Note that you must do version = ... There is no way to make a simple function call define_own_property add a property to the class being defined, because that class doesn't yet exist so you can't reference it.
Another possibility is to give your class an attribute that is a list or dict or something containing the relevant parameters ("data", "version", etc.), then write a class decorator that reads these parameters and auto-creates the series of properties. This would remove the need to define the properties inside the class at all; you would just give a list of the things you wanted the properties to access, and use the decorator once on the class.

It seems like you could use a descriptor:
class Descr(object):
def __init__(self,attr,key):
self.attr = attr
self.key = key
def __get__(self,obj,type=None):
return getattr(obj,self.attr)[self.key]
def __set__(self,obj,value):
getattr(obj,self.attr)[self.key] = value
def __delete__(self,obj):
del getattr(obj,self.attr)[self.key]
class MyClass(object):
def __init__(self, data):
self.data = data
version = Descr("data","version")
foobar = Descr("data","foobar")
a = MyClass({})
a.version = 1
print a.version
a.foobar = 'string'
print a.data

Related

Return a custom value when a class method is accessed as an attribute, but still allow for it to perform a computation when called?

Specifically, I would want MyClass.my_method to be used for lookup of a value in the class dictionary, but MyClass.my_method() to be a method that accepts arguments and performs a computation to update an attribute in MyClass and then returns MyClass with all its attributes (including the updated one).
I am thinking that this might be doable with Python's descriptors (maybe overriding __get__ or __call__), but I can't figure out how this would look. I understand that the behavior might be confusing, but I am interested if it is possible (and if there are any other major caveats).
I have seen that you can do something similar for classes and functions by overriding __repr__, but I can't find a similar way for a method within a class. My returned value will also not always be a string, which seems to prohibit the __repr__-based approaches mentioned in these two questions:
Possible to change a function's repr in python?
How to create a custom string representation for a class object?
Thank you Joel for the minimal implementation. I found that the remaining problem is the lack of initialization of the parent, since I did not find a generic way of initializing it, I need to check for attributes in the case of list/dict, and add the initialization values to the parent accordingly.
This addition to the code should make it work for lists/dicts:
def classFactory(parent, init_val, target):
class modifierClass(parent):
def __init__(self, init_val):
super().__init__()
dict_attr = getattr(parent, "update", None)
list_attr = getattr(parent, "extend", None)
if callable(dict_attr): # parent is dict
self.update(init_val)
elif callable(list_attr): # parent is list
self.extend(init_val)
self.target = target
def __call__(self, *args):
self.target.__init__(*args)
return modifierClass(init_val)
class myClass:
def __init__(self, init_val=''):
self.method = classFactory(init_val.__class__, init_val, self)
Unfortunately, we need to add case by case, but this works as intended.
A slightly less verbose way to write the above is the following:
def classFactory(parent, init_val, target):
class modifierClass(parent):
def __init__(self, init_val):
if isinstance(init_val, list):
self.extend(init_val)
elif isinstance(init_val, dict):
self.update(init_val)
self.target = target
def __call__(self, *args):
self.target.__init__(*args)
return modifierClass(init_val)
class myClass:
def __init__(self, init_val=''):
self.method = classFactory(init_val.__class__, init_val, self)
As jasonharper commented,
MyClass.my_method() works by looking up MyClass.my_method, and then attempting to call that object. So the result of MyClass.my_method cannot be a plain string, int, or other common data type [...]
The trouble comes specifically from reusing the same name for this two properties, which is very confusing just as you said. So, don't do it.
But for the sole interest of it you could try to proxy the value of the property with an object that would return the original MyClass instance when called, use an actual setter to perform any computation you wanted, and also forward arbitrary attributes to the proxied value.
class MyClass:
_my_method = whatever
#property
def my_method(self):
my_class = self
class Proxy:
def __init__(self, value):
self.__proxied = value
def __call__(self, value):
my_class.my_method = value
return my_class
def __getattr__(self, name):
return getattr(self.__proxied, name)
def __str__(self):
return str(self.__proxied)
def __repr__(self):
return repr(self.__proxied)
return Proxy(self._my_method)
#my_method.setter
def my_method(self, value):
# your computations
self._my_method = value
a = MyClass()
b = a.my_method('do not do this at home')
a is b
# True
a.my_method.split(' ')
# ['do', 'not', 'do', 'this', 'at', 'home']
And today, duck typing will abuse you, forcing you to delegate all kinds of magic methods to the proxied value in the proxy class, until the poor codebase where you want to inject this is satisfied with how those values quack.
This is a minimal implementation of Guillherme's answer that updates the method instead of a separate modifiable parameter:
def classFactory(parent, init_val, target):
class modifierClass(parent):
def __init__(self, init_val):
self.target = target
def __call__(self, *args):
self.target.__init__(*args)
return modifierClass(init_val)
class myClass:
def __init__(self, init_val=''):
self.method = classFactory(init_val.__class__, init_val, self)
This and the original answer both work well for single values, but it seems like lists and dictionaries are returned as empty instead of with the expected values and I am not sure why so help is appreciated here:

Dynamically creating properties of a class

Take a look at this code snippet:
class Face():
pass
class Cube():
def __init__(self):
self.faces = {
'front': Face(1),
...
}
#property
def front(self):
return self.faces['front']
#front.setter
def front(self, f):
pass
I've created getters and setters for all the faces. Is there any way to make this code more compact, maybe by dynamically creating the getters and setters?
The following code assumes that you
have a reason to have the self.faces dict instead of setting attributes like front directly on the instance
and/or want to implement some meaningful getter and setter logic for the keys in self.faces.
Otherwise, this exercise is pretty pointless because as Corentin Limier noted you can simply set self.front = Face(1), and so on.
You can use descriptors, a class variable holding the face names and a class decorator. Think of descriptors as reusable properties.
In the following sample code I added a num instance variable to Face and the face 'side' just for demonstration purposes.
class FaceDescriptor:
def __get__(self, instance, owner):
# your custom getter logic
# dummy implementation
if instance is not None:
return instance.faces[self.face]
def __set__(self, instance, value):
# your custom setter logic
# dummy implementation
instance.faces[self.face] = value
def set_faces(cls):
for face in cls._faces:
desc = FaceDescriptor()
desc.face = face
setattr(cls, face, desc)
return cls
class Face():
def __init__(self, num):
self.num = num
#set_faces
class Cube():
_faces = ['front', 'side']
def __init__(self):
self.faces = {face:Face(i) for i, face in enumerate(self._faces, 1)}
In action:
>>> c = Cube()
>>> c.front.num
1
>>> c.side.num
2
>>> c.front = 'stuff'
>>> c.front
'stuff'
>>> c.faces
{'front': 'stuff', 'side': <__main__.Face at 0x7fd0978f37f0>}
Assuming that's all your class does, you could do something like
class Cube:
...
def __getattr__(self, name):
return self.faces[name]
def __setattr__(self, name, value):
self.faces[name] = value
if you really want to do that you could use __getattr__ and __setattr__:
class Cube:
...
def __getattr__(self, item):
return self.faces[item]
def __setattr__(self, item, value):
self.faces[item] = value
but as you set front in the __init__ methoud you could just as well make it a regular member...
Your code is redundant, since instance attributes are already stored in a dictionary which is the __dict__ property. I recognize that you are focused on writing your code in fewer lines. It is a good challenge to keep yourself growing, but in the long term you should be focused on the clarity of your code instead.
Here is a simpler way to write your code without using properties:
class Face():
pass
class Cube():
def __init__(self):
self.front = Face()
self.rear = Face()
It is a tenet of encapsulation that you should hide your "attributes" behind "properties". Even though this isn't strongly enforced in python, it's not a bad idea to do that. Here's the proper way to do that:
class Face():
pass
class Cube():
def __init__(self):
self._front = Face()
#property
def front(self):
return self._front
#front.setter
def front(self, value):
self._front = value
To answer your question at the end, yes you can dynamically create properties.
https://stackoverflow.com/a/1355444/3368572
But keep in mind that writing dynamic code should be reserved for special cases since it will make it more difficult for your IDE to follow the flow of your program. If you use the conventions as they are intended then your code becomes self-explanatory to people and to your IDE.

Ruby like DSL in Python

I'm currently writing my first bigger project in Python, and I'm now wondering how to define a class method so that you can execute it in the class body of a subclass of the class.
First to give some more context, a slacked down (I removed everything non essential for this question) example of how I'd do the thing I'm trying to do in Ruby:
If I define a class Item like this:
class Item
def initialize(data={})
#data = data
end
def self.define_field(name)
define_method("#{name}"){ instance_variable_get("#data")[name.to_s] }
define_method("#{name}=") do |value|
instance_variable_get("#data")[name.to_s] = value
end
end
end
I can use it like this:
class MyItem < Item
define_field("name")
end
item = MyItem.new
item.name = "World"
puts "Hello #{item.name}!"
Now so far I tried achieving something similar in Python, but I'm not happy with the result I've got so far:
class ItemField(object):
def __init__(self, name):
self.name = name
def __get__(self, item, owner=None):
return item.values[self.name]
def __set__(self, item, value):
item.values[self.name] = value
def __delete__(self, item):
del item.values[self.name]
class Item(object):
def __init__(self, data=None):
if data == None: data = {}
self.values = data
for field in type(self).fields:
self.values[field.name] = None
setattr(self, field.name, field)
#classmethod
def define_field(cls, name):
if not hasattr(cls, "fields"): cls.fields = []
cls.fields.append(ItemField(name, default))
Now I don't know how I can call define_field from withing a subclass's body. This is what I wished that it was possible:
class MyItem(Item):
define_field("name")
item = MyItem({"name": "World"})
puts "Hello {}!".format(item.name)
item.name = "reader"
puts "Hello {}!".format(item.name)
There's this similar question but none of the answers are really satisfying, somebody recommends caling the function with __func__() but I guess I can't do that, because I can't get a reference to the class from within its anonymous body (please correct me if I'm wrong about this.)
Somebody else pointed out that it's better to use a module level function for doing this which I also think would be the easiest way, however the main intention of me doing this is to make the implementation of subclasses clean and having to load that module function wouldn't be to nice either. (Also I'd have to do the function call outside the class body and I don't know but I think this is messy.)
So basically I think my approach is wrong, because Python wasn't designed to allow this kind of thing to be done. What would be the best way to achieve something as in the Ruby example with Python?
(If there's no better way I've already thought about just having a method in the subclass which returns an array of the parameters for the define_field method.)
Perhaps calling a class method isn't the right route here. I'm not quite up to speed on exactly how and when Python creates classes, but my guess is that the class object doesn't yet exist when you'd call the class method to create an attribute.
It looks like you want to create something like a record. First, note that Python allows you to add attributes to your user-created classes after creation:
class Foo(object):
pass
>>> foo = Foo()
>>> foo.x = 42
>>> foo.x
42
Maybe you want to constrain which attributes the user can set. Here's one way.
class Item(object):
def __init__(self):
if type(self) is Item:
raise NotImplementedError("Item must be subclassed.")
def __setattr__(self, name, value):
if name not in self.fields:
raise AttributeError("Invalid attribute name.")
else:
self.__dict__[name] = value
class MyItem(Item):
fields = ("foo", "bar", "baz")
So that:
>>> m = MyItem()
>>> m.foo = 42 # works
>>> m.bar = "hello" # works
>>> m.test = 12 # raises AttributeError
Lastly, the above allows you the user subclass Item without defining fields, like such:
class MyItem(Item):
pass
This will result in a cryptic attribute error saying that the attribute fields could not be found. You can require that the fields attribute be defined at the time of class creation by using metaclasses. Furthermore, you can abstract away the need for the user to specify the metaclass by inheriting from a superclass that you've written to use the metaclass:
class ItemMetaclass(type):
def __new__(cls, clsname, bases, dct):
if "fields" not in dct:
raise TypeError("Subclass must define 'fields'.")
return type.__new__(cls, clsname, bases, dct)
class Item(object):
__metaclass__ = ItemMetaclass
fields = None
def __init__(self):
if type(self) == Item:
raise NotImplementedError("Must subclass Type.")
def __setattr__(self, name, value):
if name in self.fields:
self.__dict__[name] = value
else:
raise AttributeError("The item has no such attribute.")
class MyItem(Item):
fields = ("one", "two", "three")
You're almost there! If I understand you correctly:
class Item(object):
def __init__(self, data=None):
fields = data or {}
for field, value in data.items():
if hasattr(self, field):
setattr(self, field, value)
#classmethod
def define_field(cls, name):
setattr(cls, name, None)
EDIT: As far as I know, it's not possible to access the class being defined while defining it. You can however call the method on the __init__ method:
class Something(Item):
def __init__(self):
type(self).define_field("name")
But then you're just reinventing the wheel.
When defining a class, you cannot reference the class itself inside its own definition block. So you have to call define_field(...) on MyItem after its definition. E.g.,
class MyItem(Item):
pass
MyItem.define_field("name")
item = MyItem({"name": "World"})
print("Hello {}!".format(item.name))
item.name = "reader"
print("Hello {}!".format(item.name))

How to format output of methods inside of a class in an efficient way?

I want to write a class with much stuff inside. Right now I want to have certain options for the output to show data in a disired format. Im my example a function inside a class should format the output (a former dictionary) as a list. But i really can't figure out how to do it...
here my try:
class TestClass(object):
def __init__(self):
pass
def return_values_as_list(self,otherfunction):
data_as_list=[ i for i in otherfunction.values()]
return data_as_list
def get_data1_from_db(self):
self.data= {'1_testkey':'1_testvalue',"1_testkey2":"1_testvalue2"}
def get_data2_from_db(self):
self.data= {'2_testkey':'2_testvalue',"2_testkey2":"2_testvalue2"}
return self.data
What i want to have at the end is something like
['1_testvalue','1_testvalue2']
when instantiation looks like the following:
testcase = TestClass()
testcase.get_data1_from_db.return_values_as_list()
Any help with that? I thought also of hooks...but i dont really know how to do that...
I think your class get... methods should be property attributes.
class TestClass(object):
def __init__(self):
pass
#property
def get_data1_from_db(self):
data= {'1_testkey':'1_testvalue',"1_testkey2":"1_testvalue2"}
return data.values()
#property
def get_data2_from_db(self):
data= {'2_testkey':'2_testvalue',"2_testkey2":"2_testvalue2"}
return data.values()
testcase = TestClass()
print testcase.get_data1_from_db # ['1_testvalue', '1_testvalue2']
print testcase.get_data2_from_db # ['2_testvalue', '2_testvalue2']
def return_values_as_list(self,otherfunction):
data_as_list= otherfunction().values()
return data_as_list
You were almost there - you just needed to call the function (add parentheses)
Instantiate with:
testcase.return_values_as_list(testcase.get_data1_from_db)

Python "callable" attribute (pseudo-property)

In python, I can alter the state of an instance by directly assigning to attributes, or by making method calls which alter the state of the attributes:
foo.thing = 'baz'
or:
foo.thing('baz')
Is there a nice way to create a class which would accept both of the above forms which scales to large numbers of attributes that behave this way? (Shortly, I'll show an example of an implementation that I don't particularly like.) If you're thinking that this is a stupid API, let me know, but perhaps a more concrete example is in order. Say I have a Document class. Document could have an attribute title. However, title may want to have some state as well (font,fontsize,justification,...), but the average user might be happy enough just setting the title to a string and being done with it ...
One way to accomplish this would be to:
class Title(object):
def __init__(self,text,font='times',size=12):
self.text = text
self.font = font
self.size = size
def __call__(self,*text,**kwargs):
if(text):
self.text = text[0]
for k,v in kwargs.items():
setattr(self,k,v)
def __str__(self):
return '<title font={font}, size={size}>{text}</title>'.format(text=self.text,size=self.size,font=self.font)
class Document(object):
_special_attr = set(['title'])
def __setattr__(self,k,v):
if k in self._special_attr and hasattr(self,k):
getattr(self,k)(v)
else:
object.__setattr__(self,k,v)
def __init__(self,text="",title=""):
self.title = Title(title)
self.text = text
def __str__(self):
return str(self.title)+'<body>'+self.text+'</body>'
Now I can use this as follows:
doc = Document()
doc.title = "Hello World"
print (str(doc))
doc.title("Goodbye World",font="Helvetica")
print (str(doc))
This implementation seems a little messy though (with __special_attr). Maybe that's because this is a messed up API. I'm not sure. Is there a better way to do this? Or did I leave the beaten path a little too far on this one?
I realize I could use #property for this as well, but that wouldn't scale well at all if I had more than just one attribute which is to behave this way -- I'd need to write a getter and setter for each, yuck.
It is a bit harder than the previous answers assume.
Any value stored in the descriptor will be shared between all instances, so it is not the right place to store per-instance data.
Also, obj.attrib(...) is performed in two steps:
tmp = obj.attrib
tmp(...)
Python doesn't know in advance that the second step will follow, so you always have to return something that is callable and has a reference to its parent object.
In the following example that reference is implied in the set argument:
class CallableString(str):
def __new__(class_, set, value):
inst = str.__new__(class_, value)
inst._set = set
return inst
def __call__(self, value):
self._set(value)
class A(object):
def __init__(self):
self._attrib = "foo"
def get_attrib(self):
return CallableString(self.set_attrib, self._attrib)
def set_attrib(self, value):
try:
value = value._value
except AttributeError:
pass
self._attrib = value
attrib = property(get_attrib, set_attrib)
a = A()
print a.attrib
a.attrib = "bar"
print a.attrib
a.attrib("baz")
print a.attrib
In short: what you want cannot be done transparently. You'll write better Python code if you don't insist hacking around this limitation
You can avoid having to use #property on potentially hundreds of attributes by simply creating a descriptor class that follows the appropriate rules:
# Warning: Untested code ahead
class DocAttribute(object):
tag_str = "<{tag}{attrs}>{text}</{tag}>"
def __init__(self, tag_name, default_attrs=None):
self._tag_name = tag_name
self._attrs = default_attrs if default_attrs is not None else {}
def __call__(self, *text, **attrs):
self._text = "".join(text)
self._attrs.update(attrs)
return self
def __get__(self, instance, cls):
return self
def __set__(self, instance, value):
self._text = value
def __str__(self):
# Attrs left as an exercise for the reader
return self.tag_str.format(tag=self._tag_name, text=self._text)
Then you can use Document's __setattr__ method to add a descriptor based on this class if it is in a white list of approved names (or not in a black list of forbidden ones, depending on your domain):
class Document(object):
# prelude
def __setattr__(self, name, value):
if self.is_allowed(name): # Again, left as an exercise for the reader
object.__setattr__(self, name, DocAttribute(name)(value))

Categories

Resources