本文为原创文章,未经本人允许,禁止转载。转载请注明出处。
1.前言
Python中的类(Class)用来描述具有相同的属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法。⚠️对象是类的实例。
2.创建类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Employee:
'class information'
empCnt=0
def __init__(self,name,salary):
self.name=name
self.salary=salary
Employee.empCnt+=1
def displayCnt(self):
print("Total Employee %d" % Employee.empCnt)
def displayEmp(self):
print("Name : ",self.name," , Salary : ",self.salary)
Employee
为类名。'class information'
为类文档字符串,存储类的帮助信息,可以通过Employee.__doc__
查看。__init__()
被称为类的构造函数或初始化方法,当创建了这个类的实例时就会调用该方法。displayCnt()
和displayEmp()
为方法,即类内定义的函数。
3.类的基本用法
- 类:对具有相同数据和方法的一组对象的描述或定义。
- 对象:对象是一个类的实例。
- 实例(instance):一个对象的实例化实现。
- 实例属性(instance attribute):一个对象就是一组属性的集合。
- 实例方法(instance method):所有存取或者更新对象某个实例一条或者多条属性的函数的集合。
- 类属性(class attribute):属于一个类中所有对象的属性,不会只在某个实例上发生变化。
- 类方法(class method):那些无须特定的对象实例就能够工作的从属于类的函数。
3.1.self
❗️类的方法与普通的函数只有一个特别的区别:类的方法必须有一个额外的第一个参数名称,通常为self
。
1
2
3
4
5
6
7
8
class Test:
def prt(self):
print(self)
t=Test()
t.prt()
print(id(t))
print(hex(id(t)))
hex
将十进制整数转为十六进制。
输出为:
1
2
3
<__main__.Test object at 0x10dce4128>
4526588200
0x10dce4128
可以看出t
和self
的地址其实是一样的,也就是说self
代表类的实例,代表当前对象的地址,而非类。
self
不是python的关键字,也可以换成其他:
1
2
3
4
5
6
7
8
class Test:
def prt(xxx):
print(xxx)
t=Test()
t.prt()
print(id(t))
print(hex(id(t)))
结果是一样的。
3.2.__init__
方法
__init__
是一个特殊的方法,每当创建新实例时,Python都会自动运行它。在这个方法的名称中,开头和末尾各有两个下划线,这是一种约定,旨在避免Python默认方法与普通方法发生名称冲突。
以Employee
类为例,我们将方法__init__()
定义成了包含三个形参:self
、name
和salary
。⚠️在这个方法的定义中,形参self
必不可少,还必须位于其他形参的前面。
我们将通过实参向Employee()
传递name
和salary
;self
会自动传递,因此我们不需要传递它。每当我们根据Employee()
类创建实例时,都只需给最后两个形参(name
和salary
)提供值。
3.3.创建实例对象
1
2
3
4
#创建Employee类的第一个对象
emp1=Employee("Zara",2000)
#创建Employee类的第二个对象
emp2=Employee("Manni",5000)
与C++类似,类对象需要通过点运算符.
来访问类内成员:
1
2
3
emp1.displayEmp()
emp2.displayEmp()
print("Total Employee %d " % Employee.empCnt)
输出为:
1
2
3
Name : Zara , Salary : 2000
Name : Manni , Salary : 5000
Total Employee 2
👉添加、删除、修改类的属性,例如:
1
2
3
4
emp1.age=7#为实例emp1添加一个'age'属性,emp2没有该属性
emp1.age=8#修改'age'属性
print(emp1.age)#输出为:8
del emp1.age#删除'age'属性
👉也可以使用以下函数访问属性:
getattr(obj,name)
:访问对象的属性。hasattr(obj,name)
:检查是否存在某属性。setattr(obj,name,value)
:设置一个属性。如果属性不存在,会创建一个新属性。delattr(obj,name)
:删除属性。
1
2
3
4
setattr(emp2,'age',10)#相当于emp2.age=10
getattr(emp2,'age')#返回'age'属性的值,即10
hasattr(emp2,'age')#如果存在'age'属性则返回True
delattr(emp2,'age')#删除属性'age'
3.4.类的内置属性
__name__
:类的名字(字符串)。__doc__
:类的文档字符串。__bases__
:类的所有父类组成的元组。__dict__
:类的属性组成的字典。__module__
:类所属的模块。__class__
:类对象的类型。
以Employee
类为例,每个内置属性的内容见下:
1
2
3
4
5
6
Employee.__doc__: class information
Employee.__name__: Employee
Employee.__module__: __main__
Employee.__bases__: (<class 'object'>,)
Employee.__dict__: {'__module__': '__main__', '__doc__': 'class information', 'empCnt': 2, '__init__': <function Employee.__init__ at 0x10f9d8d90>, 'displayCnt': <function Employee.displayCnt at 0x10f9d8e18>, 'displayEmp': <function Employee.displayEmp at 0x10f9d8ea0>, '__dict__': <attribute '__dict__' of 'Employee' objects>, '__weakref__': <attribute '__weakref__' of 'Employee' objects>}
Employee.__class__: <class 'type'>
4.继承
编写类时,并非总是要从空白开始。如果你要编写的类是另一个现成类的特殊版本,可使用继承。一个类继承另一个类时,它将自动获得另一个类的所有属性和方法;原有的类称为父类(或基类、超类),而新类称为子类(或派生类)。子类继承了其父类的所有属性和方法,同时还可以定义自己的属性和方法。
4.1.子类的方法__init__()
我们首先新建一个表示汽车的类,它存储了有关汽车的信息,还有一个汇总这些信息的方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#electric_car.py
class Car():
'''一次模拟汽车的简单尝试'''
def __init__(self,make,model,year):
self.make=make#汽车品牌
self.model=model#汽车型号
self.year=year#生产年份
self.odometer_reading=0#里程数
def get_descriptive_name(self):
long_name=str(self.year)+' '+self.make+' '+self.model
return long_name.title()
def read_odometer(self):
print("This car has "+str(self.odometer_reading)+" miles on it.")
def update_odometer(self,mileage):
if mileage>=self.odometer_reading:
self.odometer_reading=mileage
else:
print("You can't roll back an odometer!")
def increment_odometer(self,miles):
self.odometer_reading+=miles
下面来模拟电动汽车。电动汽车是一种特殊的汽车,因此我们可以在前面创建的Car
类的基础上创建新类ElectricCar
,这样我们就只需为电动汽车特有的属性和行为编写代码。
1
2
3
4
5
6
7
8
#electric_car.py
#接着父类继续写
class ElectricCar(Car):
'''电动汽车的独特之处'''
def __init__(self,make,model,year):
'''初始化父类的属性'''
super().__init__(make,model,year)
⚠️创建子类时,父类必须包含在当前文件中,且位于子类前面。
我们定义了子类ElectricCar
,定义子类时,必须在括号内指定父类的名称。
super()
是一个特殊的函数,帮助python将父类和子类关联起来。这条语句让python调用ElectricCar
的父类的方法__init__()
。也正是因为super()
,父类也称为超类。
通过以下代码测试下子类是否成功继承了父类的方法:
1
2
my_tesla=ElectricCar('tesla','model s',2016)
print(my_tesla.get_descriptive_name())
输出为:
1
2016 Tesla Model S
4.2.给子类定义属性和方法
让一个类继承另一个类后,可添加区分子类和父类所需的新属性和方法。
1
2
3
4
5
6
7
8
9
10
11
class ElectricCar(Car):
'''电动汽车的独特之处'''
def __init__(self,make,model,year):
'''初始化父类的属性'''
super().__init__(make,model,year)
self.battery_size=70
def describe_battery(self):
'''打印一条描述电瓶容量的消息'''
print("This car has a "+str(self.battery_size)+"-kWh battery.")
为子类添加了新属性self.battery_size
,并且父类Car
实例都不包含它。此外,还添加了一个名为describe_battery()
的方法。
4.3.重写父类的方法
对于父类的方法,只要它不符合子类的要求,都可对其进行重写。为此,可在子类中定义一个这样的方法,即它与要重写的父类方法同名。这样,python将不会考虑这个父类方法,而只关注你在子类中定义的相应方法。
4.4.将实例用作属性
在不断给ElectricCar
类添加细节时,我们可能会发现其中包含很多专门针对汽车电瓶的属性和方法。在这种情况下,我们可将这些属性和方法提取出来,放到另一个名为Battery
的类中,并将一个Battery
实例用作ElectricCar
类的一个属性:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Battery():
'''一次模拟电动汽车电瓶的简单尝试'''
def __init__(self,battery_size=70):
'''初始化电瓶的属性'''
self.battery_size=battery_size
def describe_battery(self):
'''打印一条描述电瓶容量的消息'''
print("This car has a "+str(self.battery_size)+"-kWh battery.")
class ElectricCar(Car):
'''电动汽车的独特之处'''
def __init__(self,make,model,year):
'''初始化父类的属性'''
super().__init__(make,model,year)
self.battery=Battery()
my_tesla=ElectricCar('tesla','model s',2016)
print(my_tesla.get_descriptive_name())
my_tesla.battery.describe_battery()
在Battery
类中,有个可选的形参battery_size
:如果没有给它提供值,电瓶容量将被设置为70。
5.导入类
python允许将类存储在模块中,然后在主程序中导入所需的模块。
5.1.导入单个类
我们新建一个car.py,里面只存放Car
类的代码。然后我们在my_car.py中导入Car
类:
1
2
3
4
5
6
7
from car import Car
my_new_car=Car('audi','a4',2016)
print(my_new_car.get_descriptive_name())
my_new_car.odometer_reading=23
my_new_car.read_odometer()
输出为:
1
2
2016 Audi A4
This car has 23 miles on it.
5.2.在一个模块中存储多个类
除了Car
类,我们将Battery
类和ElectricCar
类也放入car.py中。并在my_electric_car.py中导入ElectricCar
类:
1
2
3
4
5
6
from car import ElectricCar
my_tesla=ElectricCar('tesla','model s',2016)
print(my_tesla.get_descriptive_name())
my_tesla.battery.describe_battery()
输出为:
1
2
2016 Tesla Model S
This car has a 70-kWh battery.
5.3.导入整个模块
1
2
3
4
5
6
import car
my_beetle=car.Car('volkswagen','beetle',2016)
print(my_beetle.get_descriptive_name())
my_tesla=car.ElectricCar('tesla','roadster',2016)
print(my_tesla.get_descriptive_name())
输出为:
1
2
2016 Volkswagen Beetle
2016 Tesla Roadster
5.4.导入模块中的所有类
导入模块中的所有类可用:
1
2
3
4
5
from car import *
my_beetle=Car('volkswagen','beetle',2016)
print(my_beetle.get_descriptive_name())
my_tesla=ElectricCar('tesla','roadster',2016)
print(my_tesla.get_descriptive_name())
与import car
的区别在于,from car import *
不再需要前缀car.
。
不推荐这种方式,因为容易造成名称冲突。
5.5.在一个模块中导入另一个模块
例如将Car
类存放在car.py中,将Battery
和ElectricCar
类存放在electric_car.py中:
1
2
3
4
5
6
7
8
#electric_car.py
from car import Car
class Battery():
#省略
class ElectricCar(Car):
#省略
6.代码地址
7.参考资料
- Python3面向对象(菜鸟教程)
- 《Python编程-从入门到实践》Eric Matthes著;袁国忠译。