如何在Python类描述符对象上调用方法?
我创建了一个如何在Python类描述符对象上调用方法?,python,descriptor,Python,Descriptor,我创建了一个类字符串(),其中包含\uuuu get\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu(),以及一;但是,当我执行name=String()时,我不能执行self.name.to\u db(),因为它调用的是到\u db(),而不是对象“name”返回的值 解决这个问题的一种方法是,不要直接调用self.name.to\u db(),而是在实例中设置一个标志,在中创
类字符串()
,其中包含\uuuu get\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu()
,以及一;但是,当我执行name=String()
时,我不能执行self.name.to\u db()
,因为它调用的是到\u db()
,而不是对象“name
”返回的值
解决这个问题的一种方法是,不要直接调用self.name.to\u db(),而是在实例中设置一个标志,在中创建一个条件来检查它,如果为True
,则调用to\u db()
,但这似乎很麻烦。有更好的办法吗
另外,我还不熟悉描述符——使用实例和/或实例的优点/缺点是什么?\uu dict\uuu
存储状态与将状态存储在self
中?内部方法到\u db您可以通过
self.__dict__['value'] # value as key is not ideal, but that's what OP used
或者,如果仅使用新样式类
object.__set__(self, name, value)
由于您使用的是magic属性,因此访问magic\uuu dict\uuu
是完全合理的
这在\uuuuu setattr\uuuuuu
[1]的文档中也有提及(很抱歉,在\uuuu set\uuuuuuuu
中没有直接引用\uuuuuuuuu dict\uuuuuuuuuuuuuuuuuu>,但它是同一个问题域)
[1] 这里有一个解决方案,允许您绕过类中定义的任何描述符:
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\u fieldname.method()
,而不是fieldname.method()
,并且您必须为需要访问此字段的所有类使用元类AllowByPassableDescriptor。同样,这是一个非常兼容的解决方案,可以避免对描述符包装的对象进行猴子式修补
也就是说,我不确定描述符是否是您尝试执行的操作(写入数据库?)的最佳解决方案。这非常简单-只要让描述符返回字符串的子类以及您想要的额外方法即可
def __get__(self, instance, owner):
class TaggedString(str):
def to_db(self):
return {'type':'string', 'value': self}
return TaggedString(self.value)`
描述符用于描述“它是什么”或“它是如何工作的”
例如,我们可以在\uuuuu get\uuuuuuuuuuuuuuuuuuuuu()
或\uuuuuuuuuuuuuuuu()
中设置一些限制
根据您的问题,我认为您希望在类型中添加自己的方法,而不是描述如何设置或获取实例
所以你可以用下面的代码来表达你想做什么
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'}
对于那些首先需要显式方法调用的对象,不使用描述符如何?在我看来,您试图以错误的方式使用描述符。下面是一个很好的说明:,特别要注意property()的实现。为什么不使用properties?String
描述符对象将在所有示例
对象之间共享。因此,如果创建exp1=Example(“Example1”)
和exp2=Example(“Example2”)
,则exp1.name
和exp2.name
将给出相同的输出'Example2'
。按照@jochenritzel使用属性
,这充其量是一个令人讨厌的解决方法。我会避免去摆弄\uu dict\uuuu
。也许我遗漏了什么,但是e=Example(name=“Julie”,age=“21”)然后e.name.to\u db()说:>>>e.name.to\u db()回溯(最后一次调用):文件“”,在AttributeError中的第1行:“str”对象没有属性“to\u db”“--可能是因为get是先被调用的。根据我上面的评论,它不能完全工作。如果您真的想使用描述符-使用带有标签的实例
参数来获取每个实例级别的值
。
def __get__(self, instance, owner):
class TaggedString(str):
def to_db(self):
return {'type':'string', 'value': self}
return TaggedString(self.value)`
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'}