9

我希望能够在具有插槽优化的类中使用 python 描述符:

class C(object):    
    __slots__ = ['a']
    a = MyDescriptor('a')
    def __init__(self, val):
        self.a = val

我遇到的问题是如何实现描述符类,以便能够将值存储在调用描述符对象的类实例中。通常的解决方案类似于下面的解决方案,但由于在 C 类中调用“slots”时不再定义“dict”,因此无法正常工作:

class MyDescriptor(object):
    __slots__ = ['name']    
    def __init__(self, name_):
        self.name = name_
    def __get__(self, instance, owner):
        if self.name not in instance.__dict__:
            raise AttributeError, self.name
        return instance.__dict__[self.name]     
    def __set__(self, instance, value):
        instance.__dict__[self.name] = value
4

3 回答 3

12

不要声明与插槽和实例方法相同的名称。使用不同的名称,并将插槽作为属性访问,而不是通过__dict__.

class MyDescriptor(object):
    __slots__ = ['name']
    def __init__(self, name_):
        self.name = name_
    def __get__(self, instance, owner):
        return getattr(instance, self.name)
    def __set__(self, instance, value):
        setattr(instance, self.name, value)

class C(object):
    __slots__ = ['_a']
    a = MyDescriptor('_a')
    def __init__(self, val):
        self.a = val

foo = C(1)
print foo.a
foo.a = 2
print foo.a
于 2011-02-06T09:51:32.503 回答
3

尽管价值可疑,但如果可以将第一个放在子类中,则以下技巧将起作用__slots__

class A( object ):
    __slots__ = ( 'a', )

class B( A ):
    __slots__ = ()

    @property
    def a( self ):
        try:
            return A.a.__get__( self )
        except AttributeError:
            return 'no a set'

    @a.setter
    def a( self, val ):
        A.a.__set__( self, val )

(您可以使用自己的描述符而不是属性。)使用这些定义:

>>> b = B()
>>> b.a
'no a set'
>>> b.a = 'foo'
>>> b.a
'foo'

据我了解,__slots__它是用自己的描述符实现的,因此__slots__同一个类中的另一个描述符只会覆盖。如果您想详细说明此技术,您可以在self.__class__.__mro__(或从instance您自己的__get__)中搜索候选描述符。

后记

好的...好吧,如果您真的想使用一个类,则可以使用以下改编:

class C( object ):
    __slots__ = ( 'c', )

class MyDescriptor( object ):

    def __init__( self, slots_descriptor ):
        self.slots_descriptor = slots_descriptor

    def __get__( self, inst, owner = None ):
        try:
            return self.slots_descriptor.__get__( inst, owner )
        except AttributeError:
            return 'no c'

    def __set__( self, inst, val ):
        self.slots_descriptor.__set__( inst, val )

C.c = MyDescriptor( C.c )

如果您坚持不可理解性,您可以在元类或类装饰器中进行分配。

于 2011-11-14T03:31:43.927 回答
1

@Glenn Maynard 的回答很好。

但我想指出 OP 问题中的一个问题(我无法对他的问题添加评论,因为我还没有足够的声誉):

__dict__当实例没有变量时,以下测试会引发错误:

        if self.name not in instance.__dict__:

因此,这是一个通用解决方案,它首先尝试访问__dict__变量(无论如何这是默认值),如果失败,请使用getattrand setattr

class WorksWithDictAndSlotsDescriptor:

    def __init__(self, attr_name):
        self.attr_name = attr_name

    def __get__(self, instance, owner):
        try:
            return instance.__dict__[self.attr_name]
        except AttributeError:
            return getattr(instance, self.attr_name)

    def __set__(self, instance, value):
        try:
            instance.__dict__[self.attr_name] = value
        except AttributeError:
            setattr(instance, self.attr_name, value)

(仅当attr_name与实际实例变量的名称不同时才有效,否则您将RecursionError在接受的答案中指出 a)

(如果同时存在__slots__AND将无法按预期工作__dict__

希望这可以帮助。

于 2017-08-26T02:54:37.460 回答