python,__slots__和"属性是只读的"

gri*_*yvp 19 python exception descriptor readonly-attribute

我想在python中创建一个具有一些属性的对象,我想保护自己不会意外使用错误的属性名称.代码如下:

class MyClass( object ) :
    m = None # my attribute
    __slots__ = ( "m" ) # ensure that object has no _m etc

a = MyClass() # create one
a.m = "?"  # here is a PROBLEM
Run Code Online (Sandbox Code Playgroud)

但运行这个简单的代码后,我得到一个非常奇怪的错误:

Traceback (most recent call last):
  File "test.py", line 8, in <module>
    a.m = "?"
AttributeError: 'test' object attribute 'm' is read-only
Run Code Online (Sandbox Code Playgroud)

有没有明智的程序员能够节省一些时间并让我了解"只读"错误?

Aym*_*ieh 43

使用声明实例变量时__slots__,Python会将描述符对象创建为具有相同名称的类变量.在您的情况下,此描述符将被m您在以下行定义的类变量覆盖:

  m = None # my attribute
Run Code Online (Sandbox Code Playgroud)

以下是您需要做的事情:不要定义一个名为的变量m,并m__init__方法中初始化实例变量.

class MyClass(object):
  __slots__ = ("m",)
  def __init__(self):
    self.m = None

a = MyClass()
a.m = "?"
Run Code Online (Sandbox Code Playgroud)

作为旁注,具有单个元素的元组在元素之后需要逗号.两者都适用于您的代码,因为它__slots__接受单个字符串或可迭代/字符串序列.通常,要定义包含元素的元组1,请使用(1,)1,不使用(1).

  • 因为此上下文中的m是类变量,而不是实例变量.为了利用描述符的set()方法,您应该分配给对象的m,而不是类.如果要初始化对象的实例变量,请在__init__方法中执行,就像我在代码片段中所做的那样. (7认同)

nik*_*kow 14

你完全是在滥用__slots__.它会阻止__dict__为实例创建.这只有在遇到许多小对象的内存问题时才有意义,因为摆脱__dict__可以减少占用空间.这是99.9%的情况下不需要的核心优化.

如果您需要您所描述的那种安全性,那么Python确实是错误的语言.更好地使用像Java这样严格的东西(而不是尝试用Python编写Java).

如果你无法弄清楚为什么类属性在你的代码中引起了这些问题,那么你可能应该三思而行,就像这样引入语言黑客.首先要更熟悉这门语言可能会更明智.

为了完整起见,这里是插槽文档链接.

  • +1表示完整性.正如所提到的那样,__slots__并不是一个坏主意,但需要谨慎使用.它不仅仅是一个安全检查,并且增加了实现的复杂性,需要注意如何使用该对象. (4认同)

Ric*_*dle 7

__slots__使用实例变量,而你拥有的是一个类变量.这就是你应该这样做的方式:

class MyClass( object ) :
  __slots__ = ( "m", )
  def __init__(self):
    self.m = None

a = MyClass()
a.m = "?"       # No error
Run Code Online (Sandbox Code Playgroud)


S.L*_*ott 6

考虑一下.

class SuperSafe( object ):
    allowed= ( "this", "that" )
    def __init__( self ):
        self.this= None
        self.that= None
    def __setattr__( self, attr, value ):
        if attr not in self.allowed:
            raise Exception( "No such attribute: %s" % (attr,) )
        super( SuperSafe, self ).__setattr__( attr, value )
Run Code Online (Sandbox Code Playgroud)

更好的方法是使用单元测试进行此类检查.这是一个相当大的运行时间开销.

  • 无法添加新的实例变量是__slots__的副作用而不是目的.(这是性能优化). (4认同)
  • 但与__slots__相比,这是更多的代码行?为什么要手动检查可以通过语言自动执行的操作. (3认同)
  • 避免"非常奇怪的错误"消息,避免花费大量时间调试__slots__的内在奥秘.我更喜欢明显而简单的解决方案,其中不需要秘密知识来进行调试. (3认同)