类名和文件名
我之前是学Java的,现在在看官方的Python教程,但对Python的源文件名称和类的关系有点搞不清楚。
在Java中,文件名和主类名是一样的,后面加上.java的扩展名。那么在Python中情况是怎样的呢?在官方教程的例子里,他们写了多个类,但没有提到文件名,我有点迷茫。
我有一个文件叫test_pie.py,里面的内容是-
class ListTest:
list1 = [2, 'a', 'ab', 'c', 'aa', 0]
list2 = ['b', list1[-2:-5]]
def PrintList(self):
print list1
print list2
对于list1和list2,我得到了-
未定义的变量:list1
在:test_pie中找到列表
未定义的变量:list2
在:test_pie中找到列表
3 个回答
Java和Python是两种完全不同的编程语言,它们之间有很多差异,远比你想象的要多。
在Java中,文件名必须和主类名一样,并且以.java结尾。那么在Python中呢?
恰恰相反:文件名不一定和任何东西相同。
在官方教程的例子中,他们写了多个类,但没有提到文件名。我有点迷糊。
因为这并不重要。(注意,在Java中,你可以在一个文件中有多个类,只要其中一个类是public
并且名字对应。在Python中,public
这个概念并不存在。)
关于list1和list2,我得到了-
未定义的变量:list1 在:test_pie
未定义的变量:list2 在:test_pie
这是因为这些属性(在Java中你会称之为“字段”)属于类,而不是对象。
方法(和Java中的名字一样)在Python中也“属于”类,但它是通过对象来访问的。这就是为什么Python的方法需要一个明确的self
参数:因为my_object.do_something(x, y, z)
实际上是被转化为MyClass.do_something(my_object, x, y, z)
(大致如此;背后还有一些复杂的“绑定”机制,让你可以把my_object.do_something
当作一个对象来使用。没错,在Python中,一切都是对象,甚至函数。你还以为Java是面向对象的呢,哈哈。)
你仍然可以通过对象访问类的属性,只要你告诉Python去哪里找。在PrintList
中,list1
和list2
是不在作用域内的——因为Python实际上只有两种作用域:局部和全局。但self
在作用域内(它是一个参数,所以在局部作用域内),你可以访问self.list1
,因为在Python中,如果对象没有这个属性,查找属性时会回退到类的属性。(实际上,这比这更复杂;还有一些特殊的方法名可以插入到这个过程中,更不用说继承是如何处理的了。)
所以你可能在想,如何才能把属性放到实际的Python对象中,而不是它的类里。答案是:你只需赋值。如果你不设置任何限制,那么你可以随时以任何名字赋值。对象基本上就是一个字典。如果你熟悉Java*script*(这是一种与Java完全无关的语言),那么它的工作方式也差不多。不过,为了控制这种“疯狂”,通常会在一个特别命名的方法__init__
中设置初始属性值,这个方法会在对象创建时自动调用(如果找到了),但它不是通常意义上的构造函数;这个角色是由__new__
承担的,但在Python中你几乎不需要用到它),然后只会重新赋值给那些已经在这里设置的属性,而不是创建新的属性。
如果你有一个新式类(在3.x中是自动的;在2.x中你必须至少间接地继承内置类型object
),你可以通过为一个特别命名的类属性__slots__
指定一个值来设置限制。这个值应该是一个有效标识符名称的字符串列表。有了这个,Python会(a)利用这些信息优化对象的数据存储;(b)不允许你给对象添加其他名字的属性;(c)抑制为对象实例创建特殊的__dict__
属性,这个属性是一个将属性名映射到属性值的字典。(你还以为Java支持反射呢,哈哈。)它不会自动为命名的属性赋值;在赋值之前尝试访问它们会引发AttributeError
错误。
在Python中,一个单独的文件就是一个模块,这有点像Java中的命名空间。也就是说,你可以把一个命名空间里的所有类都放在同一个文件里。
有一个文件。就这样。文件里面的内容对于导入来说并不重要,文件名或位置对里面的代码没有影响(一般来说 - 在执行时是可以访问的,所以一些元编程会利用这个,但应该对实际的值保持中立)。
文件的内容并不局限于一个类,很多人也不会给自己设定这样的限制。Python并不是只是一种面向对象的语言,合理的时候你可以并且应该使用自由函数,模块在代码组织上被视为比类更高一层 - 如果几个类之间关系密切,它们应该放在同一个模块里。
你提到的示例代码或问题和这个无关,实际上是关于在特定文件内的作用域问题。类确实有自己的作用域,但你不能也不应该在方法中直接使用包含类的类变量 - 这样会让代码对子类中设置的新值无能为力。相反,你可以使用类方法(顺便说一下,你可能应该看看http://dirtsimple.org/2004/12/python-is-not-java.html)或者利用实例继承类的所有成员,只需在前面加上self.
。