5

我创建了一个class String()with __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__()来检查它并调用to_db()if it's True,但这似乎很笨拙。有没有更好的办法?

另外,我是描述符的新手——使用instance和/或instance.__dict__存储状态与存储状态的优缺点是self什么?

4

4 回答 4

8

这很简单——只需让你的描述符返回一个带有你想要的额外方法的字符串子类。

def __get__(self, instance, owner):
    class TaggedString(str):
        def to_db(self):
            return {'type':'string', 'value': self}
    return TaggedString(self.value)`
于 2011-08-09T11:15:54.377 回答
1

描述符用于描述“它是什么”或“它是如何工作的”。

例如,我们可以在__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'}
于 2017-01-26T08:50:00.507 回答
0

这是一个允许您绕过类中定义的任何描述符的解决方案:

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。再说一次,这是一个非常兼容的解决方案,可以避免对描述符包装的对象进行猴子修补。

也就是说,我不确定描述符是否是您正在尝试做的事情的最佳解决方案(写入数据库?)。

于 2011-05-16T22:52:02.257 回答
-1

在to_db方法中,您可以通过以下方式直接访问该值

self.__dict__['value'] # value as key is not ideal, but that's what OP used

或者,如果您只使用新样式类,

object.__set__(self, name, value)

由于您使用的是魔法属性,因此访问魔法__dict__是完全合理的。

这在 [1] 的文档中也有提及__setattr__(抱歉,没有直接提及__dict__in,__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

于 2011-05-16T21:33:08.143 回答