Python 类介绍,以及定义和使用 Python 类
前提
阅读本节的前提是已经掌握类的相关概念,你可以查看编程教程的面向对象编程,类,实例介绍一节来了解他们。
定义 Python 类
在 Python 中,类通常被定义在模块中,其语法的基本形式如下。
class <classname>:
<docstring>
<block>
- classname 部分
classname
为 Python 类的名称,他需要符合 Python 的标识符规范,不能使用关键字或保留关键字。- docstring 部分
docstring
是 Python 类的文档字符串,包含了对类的说明,这并非强制性的要求,一个 Python 类可以没有文档字符串。- block 部分
block
为 Python 类的主体代码,他和docstring
都需要使用某种空白字符进行缩进,以表示其归属于 Python 类。
Python 类的特性
与模块类似,在 Python 类中定义的变量(字段),属性,方法(函数)等内容,被称为特性(Attribute)。特性可通过形式类似于c.name
,c.name=value
的表达式进行访问,其中c
为 Python 类或对象(类的实例),name
为特性(名称,标识符),value
为特性的新值。
当然,Python 类的特性可以被称呼为变量,方法,属性,这并没有什么问题。
下面是一个简单的 Python 类Apple
,包含变量和静态方法。
# 一个简单的类 Apple
class Apple:
# 变量 variety
variety = '普通苹果'
# 静态方法 show
@staticmethod
def show():
print(Apple.variety)
# 为特性 variety 赋值
Apple.variety = '石头苹果'
# 调用 show 方法
Apple.show()
石头苹果
Python 的类特性和实例特性
在 Python 中,类的特性分为类特性和实例特性,类特性直接在 Python 类中定义,或通过表示类的参数(一般约定为cls
)来定义,而实例特性则需要通过 Python 实例,或表示实例的参数(一般约定为self
)来定义。
Python 类特性可通过类或表示类的参数(一般约定为cls
),以及 Python 实例或表示实例的参数(一般约定为self
)来访问,而 Python 实例特性则只能通过实例或表示实例的参数来访问。
为 Python 类或实例添加暂时特性
在没有限制的情况下,一个 Python 类或实例可以添加暂时存在的特性,只需要使用形式为c.name=value
的表达式即可,其中c
为类或实例,name
为添加的特性(名称,标识符),value
为特性的值。
如何查看 Python 类或实例的所有特性?
通过 Python 类,实例,对象的特殊特性__dict__
,你可以查看其对应的所有特性,包括私有特性和暂时特性在内。__dict__
是一个字典对象,其中键值对的键表示特性的名称,键值对的值表示特性的具体内容。
需要指出的是,Python 类的__dict__
与实例的__dict__
中的键值对不会重叠,类的__dict__
仅包含类特性,实例的__dict__
仅包含实例特性,不过,不排除存在分别归属于 Python 类和实例的同名特性。
我们调整之前的示例,为类Apple
添加构造器以定义实例变量weight
,并通过类添加暂时类变量price
,通过实例添加暂时实例变量price
,然后输出显示Apple
类和实例的__dict__
特性。
# 一个简单的类 Apple
class Apple:
# …
# 构造器
def __init__(self, w):
# 通过表示实例的 self 定义了实例变量 weight
self.weight = w
# …
# 为 Apple 类添加一个类变量,然后显示 Apple 类的所有特性
Apple.price = 10
print(Apple.__dict__)
# 为实例添加一个实例变量,然后显示实例的所有特性
apple = Apple(100)
apple.price = 30
print(apple.__dict__)
{'__module__': '__main__', 'variety': '石头苹果', 'show': <staticmethod(<function Apple.show at …>)>, '__init__': <function Apple.__init__ at …>, '__dict__': <attribute '__dict__' of 'Apple' objects>, '__weakref__': <attribute '__weakref__' of 'Apple' objects>, '__doc__': None, 'price': 10}
{'weight': 100, 'price': 30}
不能通过 Python 实例来直接赋值修改类特性
你可以通过 Python 实例或表示实例的参数(比如self
)来读取类特性,但无法进行赋值操作,因为赋值操作将被认为是向 Python 实例增加新的特性,这类似于试图在函数中直接赋值模块定义的变量。
在下面的代码中,书写apple.variety
等同于读取Apple.variety
,书写apple.variety='超大苹果'
等同于为实例apple
添加实例变量variety
,Apple.variety
不会被修改。
# …
# 创建 Apple 类的实例
apple = Apple(30)
# 通过实例访问类变量 variety
print(apple.variety)
# 下面的赋值语句将为实例定义新的实例变量 variety
apple.variety = '超大苹果'
# 因此,类变量 variety 并没有变化
print(Apple.variety)
石头苹果
石头苹果
定义 Python 类的变量
Python 类的变量(字段)分为类变量和实例变量,他是 Python 类的特性之一,你可以通过以下形式来定义类变量或实例变量,其中,cls
为表示 Python 类的参数,self
为表示 Python 实例的参数。
[cls|self.]<variablename>=<value>
- variablename 部分
variablename
为变量名称,他需要符合 Python 的标识符规范,不能使用 Python 关键字或保留关键字。- value 部分
value
为变量对应的值。
# 一个表示学生的类 Student
class Student:
# 类变量 count
count = 0
# 构造器
def __init__(self, n, a):
# 实例变量 name,age
self.name = n
self.age = a
定义 Python 类的方法
Python 类的方法(函数)是类的特性之一,他与 Python 模块中的函数有相似之处,可通过如下形式进行定义,其中,@classmethod
表示定义类方法,@staticmethod
表示定义静态方法。
[@classmethod|@staticmethod]
def <methname>(<parameterlist>)
<block>
- methname 部分
methname
为方法名称,他需要符合 Python 的标识符规范,不能使用 Python 关键字或保留关键字。- parameterlist 部分
parameterlist
是方法的参数列表,他定义了方法的所有参数,参数之间使用,
进行分隔。- block 部分
block
为方法的主体代码,需要使用某种空白字符进行缩进,以表示其归属于方法。
如果未指定修饰符@classmethod
或@staticmethod
,则定义的方法被称为实例方法,其第一个参数一般命名为self
,表示 Python 实例本身,通过该参数可读取写入实例特性,或读取类特性。当通过 Python 实例调用实例方法时,需要省去self
参数,当通过 Python 类调用实例方法时,需要给出self
参数。
如果指定了修饰符@classmethod
,则定义的方法为类方法,其第一个参数一般命名为cls
,表示 Python 类本身,通过该参数可以访问类特性,但不能访问实例特性。无论是通过 Python 实例还是 Python 类,调用类方法时都需要省去cls
参数。
如果指定了修饰符@staticmethod
,则定义的方法为静态方法,静态方法不像实例方法或类方法一样拥有特殊参数,因此他不能通过特殊参数来访问 Python 实例特性或类特性。
直接定义在 Python 类中的方法均为类特性
事实上,直接定义在 Python 类中的方法均为类特性,而非实例特性,无论这些方法被称为实例方法,类方法还是静态方法,他们之间的主要区别在于,首个参数表示的是 Python 实例还是 Python 类,或均不表示。
也正因为都是类特性,实例方法,类方法,静态方法既可以通过 Python 实例调用,也可以通过 Python 类调用。
函数
想要深入了解 Python 函数,你可以查看Python 函数,参数,返回值介绍,以及 Python 函数的定义和调用一节。
下面,我们为之前的Student
类增加一些方法,并分别通过类或实例来调用他们。
# 一个表示学生的类 Student
class Student:
# …
# 实例方法 info
def info(self):
print(f'{self.name} {self.age}')
# 类方法 show
@classmethod
def show(cls):
# 通过参数 cls 读取类变量 count
print(f'一共 {cls.count} 个学生')
# 静态方法 set_count
@staticmethod
def set_count(c):
# 只能通过类访问类变量
Student.count = c
print(f'学生数量被设置为 {Student.count}')
student = Student('小小', 13)
# 调用实例方法 info
student.info()
Student.info(student)
# 调用静态方法 set_count
student.set_count(-100)
Student.set_count(100)
# 调用类方法 show
student.show()
Student.show()
小小 13
小小 13
学生数量被设置为 -100
学生数量被设置为 100
一共 100 个学生
一共 100 个学生
定义 Python 类的私有特性
无论是 Python 类特性还是实例特性,如果标识符以__
开头,则这些特性即为私有特性,只能在定义他们的 Python 类中访问,不能在外部或派生类中访问。
Python 类的私有特性标识符会自动增加前缀
以__
开头的私有特性,其标识符会自动增加特定前缀,其内容为_
与类名称的组合,这一现象可通过 Python 类或 Python 实例的__dict__
特性来观察。标识符的改动仅涉及定义特性的类,这也就是无法在外部或派生类中使用私有特性的原因,因为你在访问一个并不存在的内容。
当然,在了解以上规则后,私有特性可能变得不再私有,只需要采用具有特定前缀的标识符进行访问即可。
下面的类Teacher
定义了两个私有的实例变量,他们的标识符自动添加了前缀_Teacher
。
# 一个表示教师的类 Teacher
class Teacher:
# 构造器
def __init__(self, n, a):
# 私有实例变量 __name,__age
self.__name = n
self.__age = a
teacher = Teacher('隐者', 30)
# 显示 Teacher 实例的所有特性
print(teacher.__dict__)
# 使用特殊方式访问私有变量 __name
print(teacher._Teacher__name)
{'_Teacher__name': '隐者', '_Teacher__age': 30}
隐者
定义 Python 类的构造器
Python 类的构造器是一个名为__init__
的实例方法,与其他实例方法一样,__init__
方法的第一个参数一般命名为self
,表示 Python 实例自身,其余参数可用于初始化实例变量或执行其他操作,就如同Student
类所作的那样。
当然,在 Python 的官方文档中,并没有构造器这样的说法,__init__
的含义仅为初始化,他在 Python 实例创建后被调用。
创建 Python 类的实例
要创建 Python 类的实例,你需要使用类名称和()
,()
包含了一组参数,这些参数一般与__init__
方法的参数一致(需要省去self
)。比如,之前示例中使用Student('小小',13)
创建了Student
类的实例。
如何查看 Python 实例对应的类型?
通过 Python 实例(对象)的__class__
属性(Property),可以获取该实例对应的类型(类)信息,__class__
属性的返回值是一个type
对象。
对于已定义的 Python 类来说,__class__
属性的返回值显示为<class 'type'>
,这表示已定义的 Python 类可被视为实例,其类型是type
。
# …
# 获取 Student 类和之前创建的实例 student 的类型信息
print(student.__class__)
print(Student.__class__)
<class '__main__.Student'>
<class 'type'>
Python 类的命名空间
理所当然的,Python 类应该拥有自己的命名空间,以避免类所拥有的特性的标识符与其他标识符冲突。
命名空间,作用域
想要深入了解命名空间和作用域,你可以查看编程教程的命名空间,作用域介绍一节。
Python 类的文档字符串
与 Python 函数,模块类似,Python 类也具有存储在__doc__
中的文档字符串,他是字符串字面量,是类的第一行有效代码,可以采用紧密相邻或加入空白的方式进行拼接,不包含需要运算的内容或操作。
# 一个奇怪的类
class Hello:
"""你好啊,"""'Python!'
Python 嵌套类
嵌套类是定义在另一个类,函数或方法中的 Python 类。由于作用域的一般性原则,被定义的嵌套类的可访问性,等同于同一命名空间中定义的 Python 变量。
下面定义的嵌套类World
,其可访问性等同于Hello
中的类变量。
# 一个奇怪的类
class Hello:
# …
# 一个奇怪的嵌套类
class World:
pass