Python是一种面向对象的脚本语言,所以它也提供了面向对象编程的所有基本特征:允许多继承的类继承机制、派生类可以重写它父类的任何方法、一个方法可以调用父类中同名的方法、对象可以包含任意数量和类型的数据成员。关于继承,将在下一篇博文里面介绍,本文只简单的介绍Python中的类的定义和使用。
一、类定义
最简单的类的定义形式:
class ClassName: <statement-1> . . . <statement-N>
类定义会创建一个新的命名空间,作为一个局部的作用域。在Python中,类本身就是对象,当一个类定义结束后, 一个 Class Object 就被创建。
二、类对象
类对象(Class Object)支持两种操作:属性引用 和 实例化。
属性引用
类对象的属性引用和 Python 中所有的属性引用一样,形式为:obj.name
。类对象创建后,类命名空间中所有的名字都是有效属性名,像下面这个类:
class MyClass: """A simple example class""" i = 12345 def f(self): return 'hello world'
它有一个属性 i 和 方法 f ,所以可以用MyClass.i
和 MyClass.f
进行属性引用,分别返回一个整数和一个函数对象。__doc__ 也是一个合法的属性,返回属于这个类的文档字符串。
实例化
类的实例化形式为:
x = MyClass()
创建了一个新的实例,并且将其指定给局部变量 x 。
在创建实例时,通常可能都需要有特定的初始状态,所以一个类可以定义一个名为 __init__()
的特殊方法(构造方法):
def __init__(self): self.data = []
当一个类定义了 __init__() 方法,类在实例化时会自动调用 __init__() 方法,用于创建新的类实例。 就像C++中的构造函数一样, __init__() 也可以有更多的参数,这时实例化提供给类的参数会传给 __init__() ,比如:
class student: def __init__(self, n, a): self.name = n self.age = a stu = student('Selena', 19) print(stu.name, stu.age) # 输出:Selena 19
三、实例对象
类对象实例化得到实例对象(Instance Object),实例对象只能进行 属性引用 这一种操作。合法的属性有两种:数据属性 和 方法。
数据属性
数据属性(data attributes)相当于C++中的数据成员,在Python中,数据属性不需要声明,当它们第一次指定时就会被引入:
class MyClass: i = 12345 def f(self): return 'hello world' x = MyClass() x.counter = 1 print(x.counter) del x.counter
注:在Python中每个值都是一个对象,可以通过object.__class__
来获取对象的 class (即类型),其作用与 type() 相同。
方法
在类对象中定义的函数与普通函数只有一个特别的区别:它们的第一个参数必须是self
,用以指定调用该方法的实例对象。
注意:类的方法只有被绑定到实例对象上才能够被调用。比如上面的例子中,x 是 MyClass类的一个实例对象,所以它可以直接调用 f 方法:
x.f()
为什么 f() 定义时是有一个参数的,而这里调用不需要参数呢? 因为在调用时, x 对象作为参数传递给了函数的第一个参数(即 self)。也就是说,x.f()
是严格等价于MyClass.f(x)
的。
所以在多数情况下,调用一个方法(有个 n 个参数),和调用相应的函数(也有那 n 个参数,但是再额外加入一个使用该方法的对象) 是等价的。
另外,函数也可以在 class 外定义,指定该函数对象给类中的局部变量就可以了,例如:
# Function defined outside the class def f1(self, x, y): return min(x, y) class C: f = f1 def g(self): return 'hello world' c = C() # 实例化 c.f(1,3) c.g()
四、私有成员
从C++术语上讲,Python 类的成员(包括数据成员)通常都是 public 的,并且所有的成员函数都是 virtual 的。
那么,如何在类中定义私有变量或私有方法呢?
答:在Python中规定,以两个下划线开头的名字为私有成员,不能在类的外部使用。
示例:
class A: __str = 'python' def __f(self): return self.__str def f(self): return self.__str a = A() a.__str # 'A' object has no attribute '__str' a.__f() # 'A' object has no attribute '__f' a.f() # 输出:python
附:作用域的探讨
在讲函数变量作用域时,曾经说过在一个局部作用域内重新绑定全局变量,需要使用global
声明。否则,尝试给这个变量赋值,只是会简单的创建一个新的局部变量,而不会改变那个全局变量。
这里再介绍一个nonlocal
语句,它用于指示,在外层的局部作用域中的变量可以在这里进行重新绑定。下面是一个例子:
def scope_test(): # 作用域测试 def do_local(): x = 'local x' def do_nonlocal(): nonlocal x x = 'nonlocal x' def do_global(): global x x = 'global x' x = 'test x' # 局部变量 do_local() print('After do_local():', x) do_nonlocal() print('After do_nonlocal():', x) do_global() print('After do_global():', x) scope_test() print('In global scope:', x)可以看出,局部的赋值 do_local() 并没有改变 scope_test 绑定的 x 变量,而do_nonlocal() 则改变了 scope_test 中的 x,而do_global() 则改变了模块级别的绑定,即全局变量。