如何调用Python类描述符对象的方法?
我创建了一个叫做 class String()
的类,里面有 __get__()
和 __set__()
这两个方法,还有一个叫 to_db()
的方法。但是,当我执行 name = String()
时,我不能直接使用 self.name.to_db()
,因为这时候调用的 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)
解决这个问题的一种方法是,不直接调用 self.name.to_db()
,而是在 instance
中设置一个标志,然后在 __get__()
中创建一个条件判断,如果这个标志是 True
,就调用 to_db()
,但这样做感觉有点笨拙。有没有更好的办法呢?
另外,我对描述符还很陌生,使用 instance
和/或 instance.__dict__
来存储状态和直接存储在 self
中各有什么优缺点呢?
4 个回答
这里有一个解决方案,可以让你绕过类中定义的任何描述符:
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())
需要注意的是,这个方法并不完美——你总是需要用 real_fieldname.method()
来代替 fieldname.method()
,而且你所有需要访问这个字段的类都得使用名为 AllowBypassableDescriptors 的元类。不过,这个方案还是挺兼容的,避免了对描述符所包裹的对象进行复杂的修改。
话虽如此,我不太确定描述符是否是你想要的最佳解决方案(比如说,写入数据库?)。
描述符用于说明“这是什么”或“它是如何工作的”。
举个例子,我们可以在 __get__()
或 __set__()
中设置一些限制。
根据你的问题,我觉得你想在 type<str>
中添加你自己的方法,而不是描述如何设置或获取实例。
所以你可以使用下面的代码来表达你想做的事情。
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'}
这很简单——只需要让你的描述符返回一个字符串的子类,并添加你想要的额外方法。
def __get__(self, instance, owner):
class TaggedString(str):
def to_db(self):
return {'type':'string', 'value': self}
return TaggedString(self.value)`