2011-02-06 48 views
9

我想成为其中有槽优化一类能够使用Python描述:使用Python描述与插槽

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

我的问题是如何实现的描述符类,以便能够存储调用描述符对象的类实例中的值。通常的解决方案看起来像下面的一个,但由于“字典”,将无法正常工作时,“槽”中的C级调用不再定义:

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 

回答

10

不要声明相同的名称作为插槽和作为一个实例方法。使用不同的名称,并将该插槽作为属性访问,而不是通过__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 
+0

执行后,我可以看到'C .__ dict__'被创建。这不是我们想要避免的吗?无论如何,查看文档'__slots__'已经在使用描述符接口来实现它自己的目的,所以这可能会非常困难。 – dhill

+0

使用Python 3,C只有一个mappinyproxy而不是字典。更重要的是,C的实例没有'__dict__' – Oz123

2

虽然价值令人怀疑,下面的技巧会工作,如果它是确定把第一__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__(或从您自己的__get__中的instance开始)中搜索候选描述符。

后记

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

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) 

如果你坚持费解,可以使分配在元类或类装饰器中。

1

@Glenn Maynard的答案是好的。

但我想在OP的问题一个问题点(因为我没有带足够的声誉然而,我可以给他加的问题中留言):

下面的测试中引发错误时实例还没有一个__dict__变量:

 if self.name not in instance.__dict__: 

所以,这里是一个试图进入电影到一个通用的解决方案__dict__可变第一(这是默认反正),如果失败的话,使用getattrsetattr

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作为公认的答案指出)

(不会按预期工作,如果有__slots__ AND __dict__

希望这会有所帮助。