类(Class)
关于类(Class),是一个很抽象的概念。本篇幅简单介绍关于类的一些基础内容。更多深入的内容,可详阅官方文档:
在 Python 中,所有的数据类型都可以视为对象,也可以自定义对象。自定义对象数据类型就是类(Class)的概念。
Python 的类提供了面向对象编程的所有标准特性:类继承机制允许多个基类,派生类可以覆盖基类的任何方法,一个方法可以调用基类中相同名称的方法。
类定义语法
最简单的类定义的语法如下:
class ClassName:
<statement-1>
.
.
.
<statement-N>
类定义,和函数定义类似,必须定义执行后才能生效。
类对象
类对象支持两种操作:属性引用和实例化。
这里的属性引用使用的是 Python 中所有属性引用的标准语法:obj.name
。有效的属性名称是类对象被创建存在于类命名空间的所有名称。假设,类定义如下:
class MyClass:
"""A simple example class"""
i = 123
def func(self):
return "hello world"
在这里 MyClass.i
和 Myclass.func
属于有效属性,分别返回一个整数和一个函数对象。类属性可以被赋值,所以也可以通过赋值改变 MyClass.i
的值。在这里 __doc__
也是一个有效的属性,它返回的是一个所属类的文档字符串:"A simple example class"
。
类的实例化使用函数表示法。例如,沿用上面的类:
x = MyClass()
创建类的实例,并将此对象赋值给变量 x。在这个实例化操作的过程中,创建的是一个空对象。但许多类喜欢创建带特定初始状态的自定义实例。因此,一个类能够定义一个特殊的方法:__init__()
,例如:
def __init__(self):
self.data = []
如果一个类定义了 __init__()
方法,创建新的实例的时候,会自动调用该方法。
当然,这个方法还能够有额外的参数。在这种情况下,提供给类实例化的参数都会传递给 __init__()
,例如:
>>> class Student:
... def __init__(self, name, age):
... self.name = name
... self.age = age
...
>>> s = Student('demon', 21)
>>> s.name, s.age
('demon', 21)
实例对象
从属性引用来理解实例对象。两种有效的属性名称包括:数据属性和方法。
数据属性不需要声明:它会像局部变量一样,在首次赋值的时候产生。
另一种实例属性引用称为方法。方法是从属于对象的函数。(方法并非类实例所特有的,其他对象也有方法。本篇幅若无特别说明,方法一词专指类实例对象的方法)
实例对象的有效方法名称依赖于所属的类。根据定义,一个类所有为对象的属性都定义了与实例的相应方法。
在上面自定义的 MyClass
类中,x.func
是有效的方法引用,因为 MyClass.func
是一个函数,而 x.i
并不是方法,因为 MyClass.i
并不是一个函数。但 x.func
并不是就等同于 MyClass.func
,因为 x.f
是一个方法对象,不是函数对象。
方法对象
在上面 MyClass
的例子中,执行下面的语句:
x.func()
这个时候回返回 hello world
。但是,当不想马上就调用方法时,因为 x.f
是一个方法对象,可以将其赋值给一个变量,等待后面调用。
xf = x.func
print(xf())
当执行 print
语句时,同样会返回 hello world
。
将前面的例子重新放到这里:
class MyClass:
"""A simple example class"""
i = 123
def func(self):
return "hello world"
x = MyClass()
在前面执行 x.func()
的时候,这里并没有带参数,但是在 func 的函数定义时,指定了一个 self
参数。
这就是方法特殊的地方,实例对象会作为函数的第一个参数被传入。其实调用 x.func
等同于 MyClass.func(x)
。
类和实例变量
一般来说,实例变量用于每个实例的唯一数据,类变量用于类的所有实例共享的属性和方法,如下实例:
class Dog:
# 类变量,用于所有实例共享
kind = 'canine'
def __init__(self, name):
# 实例变量,每个实例专有
self.name = name
>>> d = Dog('Emy')
>>> e = Dog('Buddy')
>>> d.kind
'canine'
>>> e.kind
'canine'
>>> d.name
'Emy'
>>> e.name
'Buddy'
由上面的例子可以看出, kind
类变量是所有实例共有的,而实例变量 name
则是每个实例独有的。
需要注意的是,共享的数据若是涉及到可变对象,往往得到的结果并不是期望的结果。例如:
class Dog:
tricks = []
def __init__(self, name):
self.name = name
def add_tricks(self, trick):
self.tricks.append(trick)
>>> d = Dog('Emy')
>>> e = Dog('Buddy')
>>> d.add_tricks('roll over')
>>> e.add_tricks('play dead')
>>> d.tricks
['roll over', 'play dead']
>>> e.tricks
['roll over', 'play dead']
在这里可以看出,列表不应该被用作类变量。正确的类设计应该使用实例变量:
class Dog:
def __init__(self, name):
self.name = name
self.tricks = []
def add_tricks(self, trick):
self.tricks.append(trick)
>>> d = Dog('Emy')
>>> e = Dog('Buddy')
>>> d.add_tricks('roll over')
>>> e.add_tricks('play dead')
>>> d.tricks
['roll over']
>>> e.tricks
['play dead']
注意事项: 数据属性会覆盖具有相同名称的方法属性。若是程序当中的代码量够大,这种情况很有可能会发生。所以可以做某些约定来最小化这种可能产生冲突的情况。例如在属性名称前加一个下划线,或者方法属性用动词来命名,名词来命名数据属性。
以上就是关于类(Class)的一部分基础内容,后续会另开篇幅继续介绍类的其他相关内容。