在Python中,__set__和__setattr__有什么区别?应该在何时使用哪个?

14

正如标题所示。

作为一个Java程序员,我习惯于:

private int A;

public void setA(int A) {
    this.A = A;
}

public int getA() {
    return this.A
}

如果需要,在Python中我该如何做到这一点?如果其中一个是使用__setattr____set__,那么另一个用于什么?

编辑: 我觉得我需要澄清一下。我知道在Python中不会在需要之前创建setter和getter。

假设我想做这样的事情:

public void setA(int A) {
    update_stuff(A);
    and_calculate_some_thing(A);
    this.A = A;
}

如何用Pythonic的方式实现这个功能?

4个回答

18
在Python中,类似这样的东西应该使用一个property来实现(只有在它们做一些有用的事情时才需要这样做)。
class Foo(object):
    def __init__(self):
        self._x = None

    @property
    def x(self):
        return self._x

    @x.setter
    def x(self,y):
        self._x = y
在这个例子中,最好只是执行(正如Edward所指出的):
class Foo(object):
     def __init__(self):
         self.x = None

由于我们的getter/setter方法实际上什么也没做……但是,当setter/getter实际上执行的不仅仅是分配/返回属性值时,属性就会变得非常有用。

它也可以使用__setattr__/__getattr__实现(但如果您的类具有多个属性,则不应该以这种方式实现,因为它很快会变得麻烦。我还猜想,使用这种方式比使用属性更慢):

class Foo(object):
    def __init__(self):
        self._x = None
    def __setattr__(self,attr,obj):
        if(attr == 'x'):
            object.__setattr__(self,'_x',obj)
        else:
            object.__setattr__(self,attr,obj)

    def __getattr__(self,attr):
        if(attr == 'x'):
            return object.__getattr__(self,'_x')
        else:
            return object.__getattr__(self,attr)

__setattr____getattr__的实际作用而言... 当你执行以下操作时,会调用__setattr__/__getattr__

myclassinstance = MyClass()
myclassinstance.x = 'foo'  #translates to MyClass.__setattr__(myclassinstance,'x','foo')
bar = myclassinstance.x    #translates to bar=MyClass.__getattr__(myclassinstance,'x')

关于 __get____set__:之前的文章已经很好地讨论了。(链接)

请注意,在Python中没有私有变量这种东西。通常情况下,如果一个类成员以一个下划线为前缀,则不应该对它进行更改(当然,除非你知道自己在做什么)。如果它以两个下划线为前缀,它将会被名称重整,这会使得在类外部更难访问。这用于防止继承中的命名空间冲突(并且这些变量通常也不应该被更改)。


良好的属性说明,但如果解释为什么应该使用属性而不是setattr/getattr会更好。 - Greg
2
@Greg:我认为,看上面的代码,如果你有多个属性,很明显哪一个更容易维护——特别是如果你在属性设置器/获取器中执行的不仅仅是简单的setattr/getattr - mgilson
我完全不反对,但清晰度很少是坏事。+1 - Greg
2
所以如果我想在属性更改时计算某些内容,出于清晰明了的原因,我应该使用属性。但是__setattr__也可以(理论上)。 - evading

9

__set__() 用于描述符,当描述符被赋值时使用。 __setattr__() 用于绑定对象的属性。除非您正在创建描述符,否则不需要使用 __set__()


谢谢你的回答!虽然简单,但解答了关于__set____setattr_的问题。我猜想__get____set__可以被用于滥用语言的有趣方式。 - Noctis Skytower

8
如果getter/setter确实像那样简单,那么您甚至不需要费心使用它们:只需使用一个实例变量即可。如果您确实需要一个执行有趣操作的getter/setter,则应该切换到属性(property),就像mgilson所描述的那样。请注意,您可以在没有任何使用您代码的东西察觉到差异的情况下,从实例变量更改为属性。也就是说,从以下内容开始:
class Foo(object):
    def __init__(self):
        self.x = None

仅在需要对属性进行get/set操作时才按照mgilson所描述的基于属性的访问器进行更改,以便在获取/设置属性时需要进行有趣的操作。


2
推荐使用简单的实例变量加1。Python不是Java,getter/setter(在Python中称为属性)的使用要少得多,只有在实际需要时才会使用。 - rubik
@rubik "../只有在实际需要时才会使用。" 这可能就是我写 "(如果需要的话)" 的原因。 - evading

5
假设您有一个名为 Master 的类,其中包含一个属性 x,它是 Slave 类的一个实例。你想在像 some_master.x = foo 这样为 x 赋值时,执行一些代码。那么这段代码应该放在哪里呢?它可以放在 Master 类中,使用 __setattr__ 方法。也可以放在 Slave 类中,这样它就能控制被赋值的内容。在这种情况下,您需要将 Slave 定义为一个 描述符 并使用 __set__ 方法。

示例:

>>> class Master(object):
...     def __setattr__(self, name, val):
...         print "run code in Master: %s" % val
... 
>>> a = Master()
>>> a.x = 123
run code in Master: 123

与此相比:
>>> class Slave(object):
...     def __set__(self, obj, val):
...         print "run code in Slave: %s" % val
... 
>>> class Master2(object):
...     x = Slave()
... 
>>> b = Master2()
>>> b.x = 345
run code in Slave: 345

回答这个问题,哪一个更符合Python风格,我会说都不是。如果你需要真正一些事情,就把它变成一个函数。显式比隐式更好。
 def update_param(a):
     update_stuff(a)
     and_calculate_some_thing(a)
     self.a = a

那么,我应该通过一个函数来更新x,而不是使用@x.setter\n def x(self, val):\n self.x=y吗?如果我只想在设置x时更新一些相关值,这似乎很麻烦? - evading
@refuser:像设计问题一样,这取决于具体的用例。通常情况下,暴露对象的状态很少是一个好主意。 - georg

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接