如何将描述符用于非静态方法?

时间:2016-09-23 19:06:06

标签: python python-2.7

我知道我可以使用描述符来更改静态属性,就像它是普通属性一样。但是,当我尝试将描述符用于普通类属性时,我最终会更改它引用的对象,而不是对象中的值。

如果我有

正常使用,考虑到方法(param)返回一个对象

class SomeClass():
    property = method(param)

我可以这样做:

instance = SomeClass()
instance.property = 3

并且能够通过属性为实例的类来处理该设置。

现在,如果我改为

class SomeClass():
    def__init__(self):
        self.property = method(param)

我做了:

instance = SomeClass()
instance.property = 3

该代码不起作用,我用3覆盖方法(param)创建的对象的引用,而不是由描述符处理该设置。

有没有办法可以在没有静态方法的情况下使用描述符?本质上,我需要能够创建类的多个实例,每个实例都有自己的唯一属性,可以使用方便的描述符方法进行更改。这可能吗?

Python版本:2.7

谢谢!

4 个答案:

答案 0 :(得分:1)

描述符提供了一种简单的机制,用于将通常的绑定函数模式变为方法。

总结一下,函数有__get__()方法,以便在作为属性访问时可以将它们转换为方法。非数据描述符将obj.f(*args)调用转换为f(obj, *args)。致电klass.f(*args)变为f(*args)

此图表总结了绑定及其两个最有用的变体:

Transformation  Called from an Object   Called from a Class
function            f(obj, *args)           f(*args)
staticmethod        f(*args)                f(*args)
classmethod         f(type(obj), *args)     f(klass, *args)

静态方法返回基础函数而不进行更改。调用c.f或C.f相当于直接查找

object.__getattribute__(c, "f") or object.__getattribute__(C, "f").    因此,该函数可以从对象或类中相同地访问。

静态方法的优点是不引用自变量的方法。

class RevealAccess(object):
    """A data descriptor that sets and returns values
       normally and prints a message logging their access.
    """
    def __init__(self, initval=None, name='var'):
        self.val = initval
        self.name = name

    def __get__(self, obj, objtype):
        print 'Retrieving', self.name
        return self.val

    def __set__(self, obj, val):
        print 'Updating', self.name
        self.val = val

>>> class MyClass(object):
...     x = RevealAccess(10, 'var "x"')
...     y = 5
...
>>> m = MyClass()
>>> m.x
Retrieving var "x"
10
>>> m.x = 20
Updating var "x"
>>> m.x
Retrieving var "x"
20
>>> m.y
5

答案 1 :(得分:0)

考虑method(param)返回描述符,您可以使用如下属性手动调用它:

class SomeClass(object):

    def __init__(self):
        self._descriptor = method(param)

    @property
    def my_attribute(self):
        return self._descriptor.__get__(self, self.__class__)

    @my_attribute.setter
    def my_attribute(self, value):
        self._descriptor.__set__(self, value)

这将允许您创建特定于实例的描述符而不是类级描述符。

我仍然没有看到你这样做的原因,因为描述符通常应该是类级别的。这是他们的观点。否则它们只是常规对象,您可以使用属性调用它们。

答案 2 :(得分:0)

属性是"计算属性"。使用描述符实现属性。如果查找对象的属性并找到描述符对象,则描述符的getter函数将计算该值。

此特殊规则仅在班级有效。

这就是原因:

class SomeClass():
    property = <a property object here>

为所有SomeClass个实例定义属性(计算属性),但是:

class SomeClass():
    def__init__(self):
        self.property = <a property object here>

定义了一个普通属性,该属性恰好是属性对象类型,但不以任何方式特殊处理。它的getter和setter被忽略(除非用户代码明确地调用它)。

没有其他方法可以使用描述符和属性在类中设置它们而不是在实例中。

更新 - 示例:

小例子:不是创建多个具有自己的描述符的实例(方法不起作用),而是每次都实例化一个新的子类。

class BaseClass:
    pass

_class_number = 0 
def class_factory(prop):
    global _class_number
    _class_number += 1
    return type('Class' + str(_class_number), (BaseClass,), dict(prop=prop))

c1 = class_factory(property(fget=lambda self: "property1"))()
print(c1.prop)
c2 = class_factory(property(fget=lambda self: "property2"))()
print(c2.prop)

答案 3 :(得分:0)

人,

感谢您的帮助。在许多很棒的建议中,我决定将 set get 方法包装起来,从而失去描述符的实用性,同时保持模块完全为我编程的实用性。我只是扩展了课程&#34; PageElement&#34;并创建了两个新方法get()和set(),它们调用它们的对应实现:

def get(self):
    return self.__get__(self.owner_page, self.owner_page.__class__)

def set(self, value):
    self.__set__(self.owner_page, value) 

感谢您的帮助!我用一种我还不知道的语言向我敞开了眼睛。我会继续挖掘。