简体   繁体   English

从类调用时,Python描述符返回自身不是值

[英]Python descriptor returning itself not value when called from class

I want to make an attribute accessible from instance and class that cannot be reassigned. 我想从实例和类中访问无法重新分配的属性。 I have the "prevent from reassigned" part tackled thanks to a metaclass. 由于元类,我有“防止重新分配”的部分。 However, now the attribute cannot be read from the class. 但是,现在无法从类中读取该属性。 How to make it so. 如何做到这一点。

Having this: 有这个:

class Meta(type):
    def __setattr__(cls, attr, val):
        if attr =="frozen":
            print "You can't unfreeze the frozen"
        else:
            cls.name = val


class Fixed(object):
    __metaclass__ = Meta
    frzn = 'I AM AWESOME'
    def holdset(_,val):
        _.frzn = _.frzn
        print "frozen is frozen not setting to ", val

    def get(_):
        return _.frzn
    frozen = property(fset=holdset,fget=get)

When calling 打电话的时候

print Fixed.frozen
print Fixed().frozen

Gives

<property object at 0x106dbeba8>
I AM AWESOME

Why doesn't it give the same? 为什么不给它一样? How to make it give the same? 如何让它给出相同的?

A property normally only works on an instance. property通常仅适用于实例。 To make a property work on a class too, you'll need to create your own descriptor instead: 要使属性也适用于类,您需要创建自己的描述符:

class ClassAndInstanceProperty(object):
    def __init__(self, fget=None, fset=None, fdel=None, doc=None):
        self.fget = fget
        self.fset = fset
        self.fdel = fdel
        if doc is None and fget is not None:
            doc = fget.__doc__
        self.__doc__ = doc

    def __get__(self, obj, objtype=None):
        if obj is None:
            obj = objtype
        if self.fget is None:
            raise AttributeError("unreadable attribute")
        return self.fget(obj)

    def __set__(self, obj, value):
        if self.fset is None:
            raise AttributeError("can't set attribute")
        self.fset(obj, value)

    def __delete__(self, obj):
        if self.fdel is None:
            raise AttributeError("can't delete attribute")
        self.fdel(obj)

    def getter(self, fget):
        return type(self)(fget, self.fset, self.fdel, self.__doc__)

    def setter(self, fset):
        return type(self)(self.fget, fset, self.fdel, self.__doc__)

    def deleter(self, fdel):
        return type(self)(self.fget, self.fset, fdel, self.__doc__)

The only difference between the above and a regular property is in the __get__ method; 上面和常规property之间的唯一区别在于__get__方法; a normal property object does (in C) the equivalent of: 普通的property对象(在C中)相当于:

def __get__(self, obj, objtype=None):
    if obj is None:
        return self
    if self.fget is None:
        raise AttributeError("unreadable attribute")
    return self.fget(obj)

eg on a class the descriptor returns self , while my version sets obj to objtype in that case. 例如,在类上,描述符返回self ,而我的版本在这种情况下将obj设置为objtype

Also note that a descriptor __set__ method is only called when working with an instance; 另请注意, 只有在处理实例时才会调用描述符__set__方法; Fixed.frozen = 'something' would not invoke the descriptor __set__ , only Fixed().frozen = 'something' would. Fixed.frozen = 'something'不会调用描述符__set__ ,只有Fixed().frozen = 'something'会。 Your metaclass __setattr__ intercepts the attribute assignment on the class instead. 您的元类__setattr__截取类的属性赋值。

You could also put a descriptor object on the metaclass , which would then be given the opportunity to have it's __set__ called for attribute assignment on the class itself. 您还可以在元类上放置一个描述符对象,然后可以在类本身上调用它的__set__来进行属性赋值。

By convention a Python descriptor returns itself when not called with an instance. 按照惯例,Python描述符在未使用实例调用时返回自身。 Properties are just a shortcut for a descriptor, to do this you need to implement your own descriptor. 属性只是描述符的快捷方式,为此,您需要实现自己的描述符。 The following should do what you want, without needing a metaclass: 以下应该做你想要的,而不需要元类:

class Frozen(object):
    def __init__(self, fixed_value):
        self.fixed_value = fixed_value

    def __get__(self, instance, owner=None):
        return self.fixed_value

    def __set__(self, instance, value):
        print "You can't unfreeze the frozen"


class Fixed(object):
    frzn = Frozen("I AM AWESOME")

You would also probably want to override the delete method which is part of the descriptor interface. 您可能还想覆盖作为描述符接口一部分的delete方法。 There are some good articles explaining exactly how descriptors work. 有一些很好的文章解释了描述符的确切作用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM