[英]How to call methods on Python class descriptor objects?
I created a class String()
with __get__()
, __set__()
, and a method to_db()
; 我用
__get__()
, __set__()
和方法to_db()
创建了一个class String()
; however, when I do name = String()
, I can't do self.name.to_db()
because it's calling to_db()
on the value returned by __get__()
, not the object " name
". 但是,当我执行
name = String()
,我不能执行self.name.to_db()
因为它在__get__()
返回的值上调用to_db()
__get__()
,而不是对象“ name
”。
class String(object):
def __init__(self, value=None):
if value:
self.value = str(value)
def __get__(self, instance, owner):
return self.value
def __set__(self, instance, value):
self.value = str(value)
def to_db(self):
return {'type':'string', 'value': self.value}
class Example(object):
name = String()
age = Integer()
def __init__(self,name,age):
self.name = name
self.age = age
def save():
data = dict(name=self.name.to_db(), age=self.age.to_db())
db.save(data)
One way to deal with this is to not call self.name.to_db()
directly and instead set a flag in instance
and create a conditional in __get__()
to check for it and call to_db()
if it's True
, but this seems kludgy. 解决这个问题的一种方法是不直接调用
self.name.to_db()
而是在instance
设置一个标志,并在__get__()
创建一个条件来检查它并调用to_db()
如果它是True
,但这似乎是kludgy 。 Is there a better way? 有没有更好的办法?
Also, I'm new to descriptors -- what are the pros/cons of using instance
and/or instance.__dict__
to store state vs storing it in self
? 此外,我是描述符的新手 - 使用
instance
和/或instance.__dict__
来存储状态与将其存储在self
中的优点/缺点是什么?
It's pretty easy - just have your descriptor return a subclass of string with the extra method(s) you want. 这很简单 - 让你的描述符返回一个字符串的子类,并带有你想要的额外方法。
def __get__(self, instance, owner):
class TaggedString(str):
def to_db(self):
return {'type':'string', 'value': self}
return TaggedString(self.value)`
Descriptors are used to describe "what is it" or "how it works". 描述符用于描述“它是什么”或“它是如何工作的”。
For example, we can put some restriction in the __get__()
or the __set__()
. 例如,我们可以在
__get__()
或__set__()
一些限制。
According to your question, I think you want to add your own method into type<str>
, not to describe how to set or get the instance. 根据您的问题,我认为您希望将自己的方法添加到
type<str>
,而不是描述如何设置或获取实例。
So you may use thee code below to express what you want to do. 因此,您可以使用下面的代码来表达您想要做的事情。
class String(str):
def __init__(self, value):
self.value = value
def to_db(self):
return {'type':'string', 'value': self.value}
ss = String('123')
print ss #123
print ss.to_db() #{'type':'string', 'value': '123'}
Here's a solution that allows you to bypass any descriptors defined in the class: 这是一个允许您绕过类中定义的任何描述符的解决方案:
class BypassableDescriptor(object):
pass
class String(BypassableDescriptor):
def __init__(self, value=None):
if value:
self.value = str(value)
def __get__(self, instance, owner):
return self.value
def __set__(self, instance, value):
self.value = str(value)
def to_db(self):
return {'type': 'string', 'value': self.value}
class Integer(BypassableDescriptor):
def __init__(self, value=None):
if value:
self.value = str(value)
def __get__(self, instance, owner):
return self.value
def __set__(self, instance, value):
self.value = int(value)
def to_db(self):
return {'type': 'integer', 'value': self.value}
class BypassDescriptor(object):
def __init__(self, descriptor):
self.descriptor = descriptor
def __getattr__(self, name):
return getattr(self.descriptor, name)
class AllowBypassableDescriptors(type):
def __new__(cls, name, bases, members):
new_members = {}
for name, value in members.iteritems():
if isinstance(value, BypassableDescriptor):
new_members['real_' + name] = BypassDescriptor(value)
members.update(new_members)
return type.__new__(cls, name, bases, members)
class Example(object):
__metaclass__ = AllowBypassableDescriptors
name = String()
age = Integer()
def __init__(self,name,age):
self.name = name
self.age = age
def save(self):
data = dict(name = self.real_name.to_db(), age = self.real_age.to_db())
Note that it's not perfect - you'll always have to call real_fieldname.method()
instead of fieldname.method()
and you'll have to use the metaclass AllowBypassableDescriptors for all your classes which need to access this field. 请注意,它并不完美 - 您将始终必须调用
real_fieldname.method()
而不是fieldname.method()
并且您必须为需要访问此字段的所有类使用元类AllowBypassableDescriptors。 Then again, it's a pretty compatible solution that avoids monkey-patching the object wrapped by the descriptor. 然后,它是一个非常兼容的解决方案,可以避免猴子修补由描述符包装的对象。
That said, I'm not sure that descriptors are the best solution for what you're trying to do (writing to a database?). 那就是说,我不确定描述符是你尝试做什么的最佳解决方案(写入数据库?)。
Inside method to_db you may access directly the value via 在内部方法to_db中,您可以直接访问值via
self.__dict__['value'] # value as key is not ideal, but that's what OP used
or, if you are using new style classes only, 或者,如果您只使用新的样式类,
object.__set__(self, name, value)
Since you are using magic attributes, accessing the magic __dict__
is perfectly reasonable. 由于您使用魔法属性,访问魔法
__dict__
是完全合理的。
This is also referred in the documentation for __setattr__
[1] (sorry, there is no direct reference to __dict__
in __set__
but it's the same problem domain) 这也被称为对文档中
__setattr__
[1](对不起,没有直接参考__dict__
在__set__
但它同样的问题域)
If __setattr__() wants to assign to an instance attribute, it should not
simply execute self.name = value — this would cause a recursive call to itself.
Instead, it should insert the value in the dictionary of instance attributes, e.g.,
self.__dict__[name] = value. For new-style classes, rather than accessing the instance
dictionary, it should call the base class method with the same name, for example,
object.__setattr__(self, name, value).
[1] http://docs.python.org/2/reference/datamodel.html#customizing-attribute-access [1] http://docs.python.org/2/reference/datamodel.html#customizing-attribute-access
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.