Can descriptors be used on properties to provide some declarative information?
I'm new to Python so forgive me if I'm not even using the right terminology... I'm using Python 3.2 and I'm trying to figure out whether I can decorate a class property with some declarative-style information.
In my mind i开发者_运维百科t would look like this:
class MyTestClass:
def __init__(self, foo):
self.foo = foo
@property
@somedeclarativeInfo("ABC",123)
def radius(self):
return self.__foo
@radius.setter
def radius(self, foo):
self.__foo = foo
There are then two different things I'd want to do with the class:
A - Be able to interact with the foo property just like any other property (simple gets and sets)
B - Be able to dynamically find properties on a particular class that are decorated with this descriptor and be able to pull out the "ABC" and 123 values, etc.
I think maybe I should be creating a descriptor to accomplish what I want, but I'm not sure if I'm on the right track, or if this can be done.
Since my background is .Net I whipped up the following example to show what I want to do, in case that helps anyone understand my goal:
using System;
using System.Reflection;
namespace SampleWithProperties
{
public class MyCustomAttribute : Attribute
{
public string Val1;
public string Val2;
public MyCustomAttribute(string val1,string val2)
{
Val2 = val2;
Val1 = val1;
}
}
public class Foo
{
[MyCustomAttribute("abc","def")]
public string PropertyA { get; set; }
[MyCustomAttribute("xyz","X")]
public int PropertyB { get; set; }
public string PropertyC { get; set; }
}
class Program
{
static void Main(string[] args)
{
// Show that we can figure out which properties have the custom attribute,
// and that we can get the values for Val1 and Val2
foreach(PropertyInfo propertyInfo in typeof(Foo).GetProperties())
{
Console.WriteLine("Found a property named "+propertyInfo.Name);
foreach(Attribute attribute in propertyInfo.GetCustomAttributes(
attributeType:typeof(MyCustomAttribute),inherit:true))
{
Console.WriteLine("Found a MyCustomAttribute on the property.");
MyCustomAttribute myCustomAttribute = attribute as MyCustomAttribute;
Console.WriteLine("Val1 = " + myCustomAttribute.Val1);
Console.WriteLine("Val2 = " + myCustomAttribute.Val2);
}
Console.WriteLine();
}
// Show that the properties can be used like normal
Foo foo = new Foo {PropertyA = "X", PropertyB = 2, PropertyC = "Z"};
Console.WriteLine("Created an instance of Foo just for fun. Its property values are "+
foo.PropertyA+","+foo.PropertyB+","+foo.PropertyC);
}
}
}
Can this be done?
There is no simple way to do what you want with properties. You can't simply set attributes on or get attributes from items protected by a property.
def declarativeInfo(*args, **kwargs):
def wrapper(obj):
for arg in args:
setattr(obj, arg, arg)
for k, v in kwargs:
setattr(obj, k, v)
return obj
return wrapper
class MyTestClass:
def __init__(self, foo):
print MyTestClass.__dict__
self.radius = self.Radius('foo')
@declarativeInfo(bar="ABC",baz=123)
class Radius(object):
def __init__(self, foo):
self.value = foo
a = MyTestClass('foo')
print a.radius.value
print a.radius.a
is the easiest way to do this. You can always, of course, make value
a property.
If you really want radius
to be a normal property, you can store the information elsewhere in a dict and retrieve it from self.propdict
or something.
OK, I wrote this question when I was first getting started with Python. I now know how to do in Python exactly what the .Net sample code I posted did. Granted, the biggest thing I didn't realize when I originally posted the question was that descriptors alter the behavior of your attributes/properties(whatever you call them). Nonetheless, we can still allow these attributes to act like properties (and not change their behavior) yet put some metadata on them with the decorator. I'm currently implementing some protocol serialization/deserialization stuff where this is going to come in handy.
class MyCustomDescriptor:
def __init__(self,val1,val2):d
self._val1 = val1
self._val2 = val2
@property
def val1(self): return self._val1
@property
def val2(self): return self._val2
def __call__(self,decorated_method_reference):
self._decorated_method_reference = decorated_method_reference
return self
def __get__(self,instance,type=None):
if not instance:
return self
return self._decorated_method_reference(instance)
class Foo:
def __init__(self,attribute_a_value,attribute_b_value,attribute_c_value):
self._attribute_a_value = attribute_a_value
self._attribute_b_value = attribute_b_value
self._attribute_c_value = attribute_c_value
@MyCustomDescriptor(val1="abc",val2="def")
def attribute_a(self): return self._attribute_a_value
@MyCustomDescriptor(val1="xyz",val2="X")
def attribute_b(self): return self._attribute_b_value
@property
def attribute_c(self): return self._attribute_c_value
# Show that by inspecting class Foo we can figure out which attribute are marked with MyCustomDescriptor and that
# we can get the values for val1 and val2. We don't even need an instance of Foo to do this. The class itself is sufficient.
print("Inspecting class Foo. Looking for attributes marked with MyCustomDescriptor...")
for attribute_name in dir(Foo):
attribute_as_object = getattr(Foo,attribute_name)
if type(attribute_as_object) == MyCustomDescriptor:
print("attribute "+attribute_name+" is decorated with MyCustomDescriptor. val1="+attribute_as_object.val1+" val2="+attribute_as_object.val2)
# Show that the properties on Foo work like normal properties. Note that I skipped implementing setters but could have done so.
foo_instance = Foo(attribute_a_value="X",attribute_b_value=2,attribute_c_value="Z")
print("Created an instance of Foo just for fun. It's property values are "+str(foo_instance.attribute_a)+", "+str(foo_instance.attribute_b)+", "+str(foo_instance.attribute_c))
The output is:
Inspecting class Foo. Looking for attributes marked with MyCustomDescriptor... attribute attribute_a is decorated with MyCustomDescriptor. val1=abc val2=def attribute attribute_b is decorated with MyCustomDescriptor. val1=xyz val2=X Created an instance of Foo just for fun. It's property values are X, 2, Z
精彩评论