Python中跟踪类实例变量的好方法是什么?

11

我是一位刚开始学习Python的C++程序员。我想知道在大型Python类中如何跟踪实例变量。我习惯于有一个 .h 文件,它会给我一个整洁的列表(包括注释)列出所有类成员。但由于Python允许您随时添加新的实例变量,那么如何跟踪它们呢?

我能想象到这样的情况:当我已经定义了一个实例变量,并且它与我正在工作的代码相隔1000行时不小心添加了一个新的实例变量。有没有标准的做法来避免这种情况?

编辑:我使用了“成员变量”这个术语,可能引起了一些混淆。我真正的意思是实例变量,并已相应地编辑了我的问题。


提醒一下:在Python中,我们通常不称之为“变量”,而是称之为绑定到“名称”的“对象”。 - user3850
1
我知道“初学者”标签总是有点用处的。;-) - Michael Kristofik
10个回答

10

我认为,避免这种情况的标准做法是不要编写距离任何东西1000行的类!

说真的,对于任何有用的类来说,那都太多了,特别是在像Python这样表达力强的语言中。使用更多标准库提供的功能,并将代码抽象成单独的模块,应该有助于保持您的代码行数。

标准库中最大的类远远低于100行!


8
首先:类属性还是实例属性?或者两者都有?=)
通常情况下,您只需在__init__中添加实例属性,在类定义中添加类属性,通常在方法定义之前...这应该可以覆盖90%的用例。
如果代码动态添加属性,那么它可能(希望如此 :-))有很好的理由...利用动态特性、内省等。除此之外,以这种方式添加属性可能比您想象的要少见。

5

Pylint 可以静态检测在 __init__ 中未被检测到的属性,以及许多其他潜在的错误。

我还建议编写单元测试并经常运行代码以检测这些类型的“哎呀”程序错误。


4

实例变量应该在类的__init__()方法中初始化。(通常情况下)

如果不可能,可以使用__dict__在运行时获取对象的所有实例变量的字典。如果你确实需要在文档中跟踪实例变量,请将你正在使用的实例变量列表添加到类的docstring中。


3

文档生成系统(例如Epydoc)可用作对象具有哪些实例/类变量的参考,如果您担心因拼写错误而意外创建新变量,则可以使用PyChecker检查代码。


3

听起来你是在谈论实例变量而不是类变量。请注意,在以下代码中,a是类变量,b是实例变量。

class foo:
  a = 0 #class variable

  def __init__(self):
    self.b = 0 #instance variable

关于你创建一个不必要的实例变量的假设,因为另一个变量距离太远:最好的解决方案是不要创建千行级别的类。如果无法避免长度,则应该让类有一个明确定义的目的,这将使您能够一次性记住所有复杂性。


2

很多从使用C、C++或其他静态类型语言的程序员面临的一个普遍问题是变量需要预先声明。事实上,当我们劝说组织中的程序员放弃C语言转向高级编程并使用Python时,这是我们听到的最大问题之一。

理论上,您确实可以在任何时候向对象添加实例变量。是的,它可能会因为打错字等原因而发生。实际上,这很少导致错误。当出现错误时,通常不难找到错误。

只要你的类不臃肿(1000行是相当巨大的!)并且有充足的单元测试,你应该很少会遇到真正的问题。如果确实出现问题,几乎可以在任何时间进入Python控制台并检查所有内容。


2

我认为这里的主要问题是你在用Python编程时还在用C++的思维方式。

在Python中,拥有一个1000行代码的类并不是一个明智的选择(虽然在C++中这种情况很常见)。

学会利用Python的动态特性,例如你可以非常创造性地将列表和字典相结合,从而省去数百行无用的代码。

例如,如果你正在将字符串映射到函数(进行分派),你可以利用函数是一等对象的事实,并创建一个类似于以下内容的字典:

d = {'command1' : func1, 'command2': func2, 'command3' : func3}
#then somewhere else use this list to dispatch
#given a string `str`
func = d[str]
func() #call the function!

在C++中,类似这样的代码需要写很多行!

实际上,我可以使用大约十几行的C++代码(加上一些boost库)来编写它。但我明白你的意思。 - Michael Kristofik

0

最简单的方法是使用一个 IDE。PyDev 是 Eclipse 的一个插件。

我并不是一个全面精通 Python 编程的专家,但通常我在 Python 中会在类定义下直接定义类成员,所以如果我添加了新成员,它们都是相关联的。

我的个人观点是,类成员应该在同一个部分中声明,出于这个特定的原因。

然而,局部作用域的变量应该在最接近使用它们的时候进行定义(除了 C 语言——我认为它仍然需要在方法开头声明变量)。


C语言自C99版本起不再要求这个。 - Roger Pate

-2

考虑使用slots

例如:

   class Foo:
     __slots__ = "a b c".split()
   x = Foo()
   x.a =1    # 正常
   x.b =1    # 正常
   x.c =1    # 正常
   x.bb = 1  # 将引发 "AttributeError: Foo instance has no attribute 'bb'"

在任何动态编程语言中,这通常是一个问题——任何不需要变量声明的语言——变量名的拼写错误将创建一个新变量而不是引发异常或导致编译时错误。Slots有助于实例变量,但无法帮助您处理模块范围变量、全局变量、局部变量等。这没有银弹;这是不必声明变量的权衡的一部分。


1
具有 slots 的类禁止属性设置是一种副作用。Guido 将其描述为“试图劫持未经创建的插槽以达到其他目的。将插槽视为一种效率技巧,而不是声明实例变量更好的方式。” - Tim Lesher
1
这不是动态编程语言的普遍问题,只有在进行声明赋值的编程语言中才会出现。例如,Groovy并不总是这样做,但它和Python一样是动态类型的。 - Ryan

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