手记

Python 类(Class)

类(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.iMyclass.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)的一部分基础内容,后续会另开篇幅继续介绍类的其他相关内容。

1人推荐
随时随地看视频
慕课网APP