如何在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'}