如何调用Python类描述符对象上的方法?

发布于 2024-11-07 18:48:14 字数 1196 浏览 6 评论 0原文

我使用 __get__()__set__() 和方法 to_db() 创建了一个 class String() ;但是,当我执行 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 中的优点/缺点是什么?

I created a class String() with __get__(), __set__(), and a method to_db(); 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".

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. 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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

过气美图社 2024-11-14 18:48:14

这非常简单 - 只需让您的描述符使用您想要的额外方法返回字符串的子类即可。

def __get__(self, instance, owner):
    class TaggedString(str):
        def to_db(self):
            return {'type':'string', 'value': self}
    return TaggedString(self.value)`

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)`
亢潮 2024-11-14 18:48:14

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

例如,我们可以在__get__()__set__()中添加一些限制。

根据您的问题,我认为您想将自己的方法添加到 type 中,而不是描述如何设置或获取实例。

因此,您可以使用下面的代码来表达您想要做的事情。

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'}

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__().

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.

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'}
喵星人汪星人 2024-11-14 18:48:14

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

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

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

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. 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?).

装迷糊 2024-11-14 18:48:14

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

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

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

object.__set__(self, name, value)

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

__setattr__ [1] 的文档中也提到了这一点(抱歉,__set__ 中没有直接引用 __dict__,但这是同样的问题域)

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

Inside method to_db you may access directly the value 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.

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)

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文