Python:self vs type(self)和类变量的正确使用

4
在Python中使用类变量时,可以通过"self"(由于引用)或"type(self)"(直接)访问和(如果它是可变的)直接操作它,而不可变的变量(例如整数)显然会在您仅使用"self"时被新实例对象所替代。
因此,在处理Python类变量时,是否最好/符合Python风格的方法是始终使用"type(self)"来处理从类方法中引用的类变量?
(我知道类变量有时会受到批评,但当我使用它们时,我希望以一种一致的方式访问它们(与不可变类型的一种方式不同,如果它们是可变的,则是另一种方式。)
编辑:是的,如果修改不可变对象的值,您将得到一个新对象作为结果。修改可变对象的值的副作用导致了这个问题 - "self"将为您提供一个引用,您可以使用它来更改类变量而不会遮蔽它,但如果您将一个新对象分配给它,它将遮蔽它。使用classname.classvar或type(self)。classvar或self.__class__确保您始终在处理类变量,而不仅仅是遮蔽它(尽管子类会使这变得复杂,如下面所述)。

你能展示一些示例代码吗?你所说的类方法是什么意思?你是指被@classmethod修饰的方法,还是指类中的普通方法? - matt
我指的是类的普通方法。下面有一些示例代码 - 看起来问题本身已经被理解了,尽管需要一些澄清。 - MartyMacGyver
4个回答

6
您可能希望使用实际类型名称来访问它们,而不是使用selftype(self)
您看到的效果与可变性无关。如果您这样做
class Foo(object):
    x = []
    def __init__(self):
        self.x = [1]

__init__ 中对 x 的赋值会创建一个新列表,并将其分配给实例属性 x,忽略类属性,即使 Foo.x 是可变对象。每当您想要分配给属性时,都需要使用实际定义的对象。

请注意,在继承的情况下,通过 type(self) 修改属性会失败:

class Foo(object):
    x = 1
    def modify_x(self, x):
        type(self).x = x

class Bar(Foo): pass

Foo().modify_x(2) # modifies Foo.x
Bar().modify_x(3) # modifies Bar.x, the wrong attribute

@MartyMacGyver: 你不能改变一个不可变的对象。你只能重新分配它,并且分配是你问题的真正根源。 - user2357112
我可以将“change”或“modify”放在引号中,但是是的,我理解这个事实。一个人可以使用self来修改可变类变量(因为引用),但是我想知道当处理类变量时,是否应该始终使用type(self).classvar(或classname.classvar)结构以确保清晰,并避免仅使用“self”带来的任何副作用。 - MartyMacGyver
@MartyMacGyver:有一些情况下,self 更可取,例如当预期子类要重写属性时。然而,当你想引用特定类的类属性时,我建议明确地引用该类。 - user2357112
"self"单独使用能否在子类中重写一个变量为一个新的变量?我可以看到它创建了一个实例变量... 唯一能起作用的是对subclass.classvar进行赋值。 - MartyMacGyver
@MartyMacGyver:不,你不能通过self创建一个类属性。我的意思是,你可以通过self访问正确的覆盖;例如,如果你有一个名为Base的类,它有一个x属性和一堆定义了自己的x值的子类,你可以通过self.x来访问正确的值。 - user2357112
显示剩余2条评论

1
python -c 'import this'
The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!

我会建议这些作为参考。
Beautiful is better than ugly.
Simple is better than complex.
Readability counts.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
If the implementation is hard to explain, it's a bad idea.

我的建议是使用直接属性引用,因为这可能是语言设计者想要的。

那真是绕了一大圈啊... 那么,直接使用 classname.classvar 或者 type(self).classvar 这样的形式呢?还是其他的写法? - MartyMacGyver
此外,type(self).classvar 在不同版本的 Python 中可能会产生不同的效果。type() 在 Python 的发展历史中经历了一些变化。 - Andrew Johnson
考虑到2.6、2.7和3.x版本,你知道是否存在任何不同的影响吗?再次,请问你推荐使用哪种构造方式——classname.classvar、type(self).classvar还是其他方式? - MartyMacGyver
我推荐使用classname.classvar。具体来说,2.6、2.7和3.x之间的元类和类型之间的交互方式已经发生了变化。即使您不打算自己使用元类,元类仍然是常见的。请参考以下链接:http://python-3-patterns-idioms-test.readthedocs.org/en/latest/Metaprogramming.html#the-metaclass-hook - Andrew Johnson
@AndrewJohnson - 谢谢你的详细说明和建议!我会更仔细地研究元类。 - MartyMacGyver

1

在与其他人线下交流后(以及根据@wwii在这里的一个答案中的评论),事实证明最好的方法是使用self.__class__.attribute而不是显式地嵌入类名。

(虽然有些人会使用type(self).attribute,但这会引起其他问题。)


这会导致与type(self).attribute相同的问题,包括继承方面的问题。 - undefined

0

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