文章目录
类
面向对象编程是最有效的软件编写方法之一。根据类来创建对象成为实例化。
创建和使用类
使用类几乎可以模拟任何东西。
创建 Dog 类
根据Dog类创建的每个实例都将存储名字和年龄,我们赋予每条小狗蹲下(sit())和打滚( roll_over())的能力。
在Python中,首字母大写的名称指的是类。
方法____init____( ) 两边都有两个下划线,否则当你使用类来创建实例时,将不会自动调用这个方法,进而引发难以发现的错误。
根据类创建实例
class Dog(): #根据约定,在Python中,首字母大写的名称指的是类。
"""一次模拟小狗的简单尝试。"""
def __init__(self,name,age):
"""初始化属性name和age。"""
self.name=name
self.age=age
def sit(self):
"""模拟小狗收到命令时蹲下。"""
print(f"{self.name} is now sitting.")
def roll_over(self):
"""模拟小狗收到命令时打滚。"""
print(f"{self.name} rolled over!")
#创建实例
my_dog=Dog("Willie",6)
print(f"My dog's name is {my_dog.name}")
print(f"My dog's is {my_dog.age} years old.")
输出:
My dog's name is Willie
My dog's is 6 years old.
class Dog(): #根据约定,在Python中,首字母大写的名称指的是类。
"""一次模拟小狗的简单尝试。""" #这个类定义中没有圆括号,因为要从空白创建这个类。
def __init__(s,a,b): #self 可写成任意字符,形参name、age可替换成任意字符
"""初始化属性name和age。"""
s.name=a
s.age=b
def sit(d): # self 可写成任意字符,a、b、s也可以
"""模拟小狗收到命令时蹲下。"""
print(f"{d.name} is now sitting.")
def roll_over(s): # self 可写成任意字符,a、b、s也可以
"""模拟小狗收到命令时打滚。"""
print(f"{s.name} rolled over!")
my_dog=Dog("Willie",6)
print(f"My dog's name is {my_dog.name}")
print(f"My dog's is {my_dog.age} years old.")
my_dog.sit()
my_dog.roll_over()
输出:
My dog's name is Willie
My dog's is 6 years old.
Willie is now sitting.
Willie rolled over!
访问属性
要访问实例的属性
,可使用句点表示法。Python先找到实例my_dog,再查找与该实例相关联的属性name。在Dog类中引用这个属性时,使用的是self.name
my_dog.name #访问my_dog的属性name的值。
输出:
'Willie'
调用方法
根据Dog类创建实例后,就能使用句点表示法来调用Dog类中定义的任何方法
了。
my_dog.sit()
my_dog.roll_over()
输出:
Willie is now sitting.
Willie rolled over!
创建多个实例
每条小狗都是 一个独立的实例,有自己的一组属性,能够执行相同的操作。
class Dog(): #根据约定,在Python中,首字母大写的名称指的是类。
"""一次模拟小狗的简单尝试。"""
"""A simple attempt to model a dog."""
def __init__(self,name,age):
"""初始化属性name和age。"""
"""Initialize name and age attributes."""
self.name=name
self.age=age
def sit(self):
"""模拟小狗收到命令时蹲下。"""
"""Simulate a dog sitting in response to a command."""
print(f"{self.name} is now sitting.")
def roll_over(self):
"""模拟小狗收到命令时打滚。"""
"""Simulate rolling over in response to a command."""
print(f"{self.name} rolled over!")
#创建实例
my_dog=Dog("Willie",6)
your_dog=Dog("Lucy",3)
print(f"My dog's name is {my_dog.name}")
print(f"My dog's is {my_dog.age} years old.")
my_dog.sit()
print(f"\nYour dog's name is {your_dog.name}")
print(f"your dog's is {your_dog.age} years old.")
your_dog.sit()
输出:
My dog's name is Willie
My dog's is 6 years old.
Willie is now sitting.
Your dog's name is Lucy
your dog's is 3 years old.
Lucy is now sitting.
使用类和实例
Car 类
编写一个表示汽车的类。它存储了有关汽车的信息,还有一个汇总这些信息的方法。
class Car:
"""一次模拟汽车的简单尝试"""
def __init__(self,make,model,year):
"""初始化描述汽车的属性"""
self.make=make
self.model=model
self.year=year
def get_descriptive_name(self):
"""返回整洁的描述性信息"""
long_name=f"{self.year} {self.make} {self.model}"
return long_name.title()
#创建实例
my_new_car=Car("audi","a4",2019)
print(my_new_car.get_descriptive_name())
输出:
2019 Audi A4
给属性指定默认值
创建实例时,有些属性无须通过形参来定义,可在方法____init____( ) 中为其指定默认值。
class Car:
"""一次模拟汽车的简单尝试"""
def __init__(self,make,model,year):
"""初始化描述汽车的属性"""
self.make=make
self.model=model
self.year=year
self.odometer_reading=0 #创建一个名为odometer_reading(里程表读数)的属性,并将其初始值设置为0
def get_descriptive_name(self):
"""返回整洁的描述性信息"""
long_name=f"{self.year} {self.make} {self.model}"
return long_name.title()
def read_odometer(self): #读 里程表
"""打印一条指出汽车里程的消息"""
print(f"This car has {self.odometer_reading} miles on it.")
my_new_car=Car("audi","a4",2019)
print(my_new_car.get_descriptive_name())
my_new_car.read_odometer()
输出:
2019 Audi A4
This car has 0 miles on it.
修改属性的值
直接修改属性的值
直接将里程表读数设置为23
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=f"{self.year} {self.make} {self.model}"
return long_name.title()
def read_odometer(self):
"""打印一条指出汽车里程的消息"""
print(f"This car has {self.odometer_reading} miles on it.")
my_new_car=Car("audi","a4",2019)
print(my_new_car.get_descriptive_name())
my_new_car.odometer_reading=23 #直接修改 里程表读数
my_new_car.read_odometer()
输出
2019 Audi A4
This car has 23 miles on it.
通过方法修改属性的值
无需直接访问属性,而可将值传递给方法,由它在内部更新。
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=f"{self.year} {self.make} {self.model}"
return long_name.title()
def read_odometer(self):
"""打印一条指出汽车里程的消息"""
print(f"This car has {self.odometer_reading} miles on it.")
def update_odometer(self,mileage): #update_odometer()在修改属性前检查指定的读数是否合理
"""
将里程表读数设置为指定的值。
禁止将里程表读书往回调
"""
if mileage>=self.odometer_reading: #如果新指定的里程mileage>=原来的里程 self.odometer_reading
self.odometer_reading=mileage #就将里程表读数改为新指定的里程
else: #否则发出警告,指出不能将里程表回调
print("You can't roll back an odometer!")
my_new_car=Car("audi","a4",2019)
print(my_new_car.get_descriptive_name())
my_new_car.update_odometer(23) #将23传递给方法update_odometer
my_new_car.read_odometer()
输出:
2019 Audi A4
This car has 23 miles on it.
通过方法对属性的值进行递增
假设购买了一辆二手车,且从购买到登记期间增加了100英里的里程
class Car:
"""一次模拟汽车的简单尝试"""
"""A simple attempt to represent a car."""
def __init__(self,make,model,year):
"""初始化描述汽车的属性"""
"""Initialize attributes to describe a car."""
self.make=make
self.model=model
self.year=year
self.odometer_reading=0
def get_descriptive_name(self):
"""返回整洁的描述性信息"""
"""Return a neatly formatted descriptive name."""
long_name=f"{self.year} {self.make} {self.model}"
return long_name.title()
def read_odometer(self):
"""打印一条指出汽车里程的消息"""
"""Print a statement showing the car's mileage."""
print(f"This car has {self.odometer_reading} miles on it.")
def update_odometer(self,mileage):
"""
将里程表读数设置为指定的值。
禁止将里程表读书往回调
"""
"""
Set the odometer reading to the given value.
Reject the change if it attempts to roll the odometer back.
"""
if mileage>=self.odometer_reading:
self.odometer_reading=mileage
else:
print("You can't roll back an odometer!")
def increment_odometer(self,miles): #将里程表读数增加指定的量
"""Add the given amount to the odometer reading."""
self.odometer_reading +=miles
my_used_car=Car("subaru","outback",2015)
print(my_used_car.get_descriptive_name())
my_used_car.update_odometer(23_500)
my_used_car.read_odometer()
my_used_car.increment_odometer(100)
my_used_car.read_odometer()
输出:
2015 Subaru Outback
This car has 23500 miles on it.
This car has 23600 miles on it.
继承
如果要编写的类是另外一个现成类的特殊版本,可使用继承。一个类继承另一个类时,将自动获得另一个类的所有属性和方法。原有的类成为父类,新类称为子类
。子类继承了父类的所有属性和方法,同时还可以定义自己的属性和方法。
子类的方法__init__()
要调用父类的方法__init__()将初始化在父类__init__()方法中定义的所有属性,从而让子类包含这些属性
继承Car类
class Car:...
#电动汽车类 #创建子类时,父类必须包含在当前文件中,且位于子类前面。
class ElectricCar(Car): #定义子类时,必须在圆括号内指定父类的名称。
"""电动汽车的独特之处"""
def __init__(self,make,model,year): #方法__init__()接受创建Car实例所需的信息。
"""初始化父类的属性"""
#self.make=make 用了super().__init__(make,model,year) 省略了3行代码
#self.model=model
#self.year=year
super().__init__(make,model,year) #调用父类的属性,不用再重新定义一遍
#super()是一个特殊函数,让你能够调用父类的方法。这行代码让Python调用Car类的.父类也称超类(superclass)
#方法__init__(),让ElectricCar实例包含这个方法中定义的所有属性。
#创建ElectricCar类的一个实例,并将其赋给变量my_tesla
#这行代码调用ElectricCar类中定义的方法__init__(),后者让Python调用Car类的方法__init__()
my_tesla=ElectricCar("tesla","model s",2019)
print(my_tesla.get_descriptive_name())
输出:
2019 Tesla Model S
给子类定义属性和方法
让一个类继承另一个类后,就可以添加区分子类和父类所需的新属性和新方法了。
编写新方法电容瓶容量75
class Car:...
class ElectricCar(Car):
"""Represent aspects of a car, specific to electric vehicles."""
def __init__(self,make,model,year):
"""
Initialize attributes of the parent class.
Then initialize attributes specific to an electric car.
"""
super().__init__(make,model,year)
#设置初始值
self.battery_size=75 #添加了新属性self.battery_size,并设置其初始值(75)
#根据ElectricCar类创建的所有实例都将包含该属性,但所有Car实例都不包含它。
#新增方法
def describe_battery(self):
"""打印一条描述电瓶容量的消息"""
"""Print a statement describing the battery size."""
print(f"This car has a {self.battery_size}-kwh battery.")
my_tesla=ElectricCar("tesla","model s",2019)
print(my_tesla.get_descriptive_name())
my_tesla.describe_battery()
输出:
2019 Tesla Model S
This car has a 75-kwh battery.
重写父类的方法
在子类中定义一个与要重写的父类方法同名的方法
对于父类的方法,只要它不符合子类模拟的实物的行为,都可以进行重写。为此,可在子类中定义一个与要 重写的父类方法同名的方法。这样,Python将不会考虑这个父类方法,而只关注你在子类中定义的相应方法。
class Car:...
def fill_gas_tank(self):
print("This car needs a gas tank.")
#电动汽车类
class ElectricCar(Car):
def __init__(self,make,model,year):
super().__init__(make,model,year) #调用父类的属性,不用在重新定义一遍
self.battery_size=75
def describe_battery(self):
print(f"This car has a {self.battery_size}-kwh battery.")
#要重写的父类方法同名的方法
def fill_gas_tank(self):
print("This car doesn't need a gas tank!")
#创建ElectricCar实例,赋给变量my_tesla
my_tesla=ElectricCar("tesla","model s",2019)
print(my_tesla.get_descriptive_name())
#Python会忽略Car类的fill_gas_tank()方法,运行ElectricCar类中的fill_gas_tank()方法
my_tesla.fill_gas_tank()
输出:
2019 Tesla Model S
This car doesn't need a gas tank!
将实例用作属性
将类的一部分提取出来,作为一个独立的类。
使用代码模拟实物时,你可能会发现自己给类添加的细节越来越多:属性和方法清单以及文件都越来越长。在这种情况下,可能需要将类的一部分提取出来,作文一个独立的类。可以将大型类拆分成多个协同工作的小类。
class Car:...
#定义一个名为Battery的新类,它没有继承任何类。
class Battery:
"""一次模拟电动汽车电瓶的简单尝试。"""
def __init__(self,battery_size=75): #形参battery_size是可选的,如果没有给它提供值,电瓶容量将被设置为75
"""初始化电瓶的属性"""
self.battery_size=battery_size
def describe_battery(self): #方法describe_battery也移到了这个类中。
"""打印一条描述电瓶容量的消息"""
print(f"This car has a {self.battery_size}-kwh battery.")
#电动汽车类
class ElectricCar(Car):
"""电动汽车的独特之处"""
def __init__(self,make,model,year):
"""
初始化父类的属性
再初始化电动汽车特有的属性
"""
super().__init__(make,model,year)
self.battery=Battery()
#添加了一个名为self.battery的属性,让Python创建一个新的Battery实例
#(因为没有指定容量,默认75),并将该实例赋给属性self.battery
#创建ElectricCar实例,赋给变量my_tesla
my_tesla=ElectricCar("tesla","model s",2019)
print(my_tesla.get_descriptive_name())
my_tesla.battery.describe_battery() #这行代码让Python在实例my_tesla中查找属性battery,
#并对存储在该属性中的Batter实例 调用方法describe_battery()
输出:
2019 Tesla Model S
This car has a 75-kwh battery.
再给Battery类添加了一个方法,它根据电瓶容量报告汽车的续航里程。
class Car:...
class Battery:
"""一次模拟电动汽车电瓶的简单尝试。"""
def __init__(self,battery_size=75):
"""初始化电瓶的属性"""
self.battery_size=battery_size
def describe_battery(self):
"""打印一条描述电瓶容量的消息"""
print(f"This car has a {self.battery_size}-kwh battery.")
def get_range(self): #添加一个方法,它根据电瓶容量报告汽车的续航里程
"""打印一条消息,指出电瓶的续航里程"""
if self.battery_size==75:
range=260
elif self.battery_size==100:
range=315
print(f"This car can go about {range} miles on a full charge.")
#电动汽车类
class ElectricCar(Car):
"""电动汽车的独特之处"""
def __init__(self,make,model,year):
"""
初始化父类的属性
再初始化电动汽车特有的属性
"""
super().__init__(make,model,year)
self.battery=Battery()
#创建ElectricCar实例,赋给变量my_tesla
my_tesla=ElectricCar("tesla","model s",2019)
print(my_tesla.get_descriptive_name())
my_tesla.battery.describe_battery()
my_tesla.battery.get_range() #为使用方法get_range(),也需要通过汽车的属性battery来调用
输出:
2019 Tesla Model S
This car has a 75-kwh battery.
This car can go about 260 miles on a full charge.
模拟实物
解决问题时,从较高的逻辑层面考虑,考虑的不是 Python,而是如何使用代码来表示实物。
导入类
导入单个类
通过将这个类移到一个模块中并导入该模块,依然可以使用所有功能,但主程序文件变得整洁而易于阅读。
先创建一个只包含Car类的模块,模块命名为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=f"{self.year} {self.make} {self.model}"
return long_name.title()
def read_odometer(self):
"""打印一条消息,指出汽车的里程"""
print(f"This car has {self.odometer_reading} miles on it.")
def update_odometer(self,mileage):
"""
将里程表读数设置为指定的值。
拒绝将里程表回调。
"""
if mileage>=self.odometer_reading:
self.odometer_readings=mileage
else:
print("You can't roll back an odometer!")
def incream_odometer(self,miles):
"""将里程表读数增加指定的量"""
self.odometer_reading +=miles
再新建空白 my_car.py文件,导入Car模块
from car import Car
my_new_car=Car("audi","a4",2019)
print(my_new_car.get_descriptive_name())
my_new_car.odometer_reading=23
my_new_car.read_odometer()
输出:
2019 Audi A4
This car has 23 miles on it.
在一个模块中存储多个类
虽然同一个模块中的类之间应存在相关性,但可根据需要在一个模块中存储任意数量的类。
先创建一个包含Car、Battery、ElectricCar类的模块,模块命名为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=f"{self.year} {self.make} {self.model}"
return long_name.title()
def read_odometer(self):
"""打印一条消息,指出汽车的里程"""
print(f"This car has {self.odometer_reading} miles on it.")
def update_odometer(self,mileage):
"""
将里程表读数设置为指定的值。
拒绝将里程表回调。
"""
if mileage>=self.odometer_reading:
self.odometer_readings=mileage
else:
print("You can't roll back an odometer!")
def incream_odometer(self,miles):
"""将里程表读数增加指定的量"""
self.odometer_reading +=miles
class Battery:
"""一次模拟电动汽车电瓶的简单尝试"""
def __init__(self,battery_size=75):
"""初始化电瓶的属性"""
self.battery_size=battery_size
def describe_battery(self):
"""打印一条描述电瓶容量的消息"""
print(f"This car has a {self.battery_size}-kwh battery.")
def get_range(self):
"""打印一条描述电瓶续航里程的消息"""
if self.battery_size==75:
range=260
elif self.battery_size==100:
range=300
print(f"This car can go about {range} miles on a full charge.")
class ElectriCar(Car):
"""模拟电动汽车的独特之处"""
def __init__(self, make, model, year):
"""
初始化父类的属性
再初始化电动汽车特有的属性
"""
super().__init__(make, model, year)
self.battery=Battery()
再新建my_electri_car.py文件,导入ElectricCar类
from car import ElectriCar
my_tesla=ElectriCar("tesla","model s",2019)
print(my_tesla.get_descriptive_name())
my_tesla.battery.describe_battery()
my_tesla.battery.get_range()
输出:
2019 Tesla Model S
This car has a 75-kwh battery.
This car can go about 260 miles on a full charge.
从一个模块中导入多个类
可根据需要在程序文件中导入任意数量的类。如果需要在同一个程序中创建普通汽车和电动汽车,就需要将Car类和ElectricCar类都导入
from car import Car, ElectricCar
my_beetle = Car("volkswagen", "beetle", 2019)
print(my_beetle.get_descriptive_name())
my_tesla = ElectricCar("tesla", "roadster", 2019)
print(my_tesla.get_descriptive_name())
输出:
2019 Volkswagen Beetle
2019 Tesla Roadster
导入整个模块
可以导入整个模块,再使用句点表示法
访问需要的类。这种导入方式很简单,代码也易于阅读。因为创建类实例的代码都包含模块名,所以不会与当前文件使用的任何名称发生冲突。
import car #导入这个car模块
my_beetle=car.Car("volkswagen","beetle",2019)
print(my_beetle.get_descriptive_name()) #使用语法module_name.ClassName访问需要的类
my_tesla=car.ElectriCar("tesla","roadster",2019)
print(my_tesla.get_descriptive_name())
输出:
2019 Volkswagen Beetle
2019 Tesla Roadster
导入模块中的所有类
from module_name import * #不推荐
需要从一个模块中导入很多类时,最好导入整个模块,并使用module_name.ClassName
语法来访问类。
在一个模块中导入另一个模块
将类分散到多个模块中,以免模块太大或在同一个模块中存储不相关的类。
将类存储在多个模块中时,你可能会发现一个模块中的类依赖于另一个模块中的类。在这种情况下,可在前一个模块中导入必要的类。
下面将Car类存储到一个模块中(car.py),并将ElectircCar类和Battery类存储在另一个模块中(my_electric_car.py)。
"""一组可用于表示电动汽车的类"""
from car import Car
class Battery:
"""一次模拟电动汽车电瓶的简单尝试"""
def __init__(self,battery_size=75):
"""初始化电瓶的属性"""
self.battery_size=battery_size
def describe_battery(self):
"""打印一条描述电瓶容量的消息"""
print(f"This car has a {self.battery_size}-kwh battery.")
def get_range(self):
"""打印一条描述电瓶续航里程的消息"""
if self.battery_size==75:
range=260
elif self.battery_size==100:
range=300
print(f"This car can go about {range} miles on a full charge.")
class ElectriCar(Car):
"""模拟电动汽车的独特之处"""
def __init__(self, make, model, year):
"""
初始化父类的属性
再初始化电动汽车特有的属性
"""
super().__init__(make, model, year)
self.battery=Battery()
分别从每个模块中导入类,以根据需要创建任何类型的汽车:
from car import Car
from electric_car import ElectriCar
my_beetle=Car("volkswagen","beetle",2019)
print(my_beetle.get_descriptive_name())
my_tesla=ElectriCar("tesla","roadster",2019)
print(my_tesla.get_descriptive_name())
输出:
2019 Volkswagen Beetle
2019 Tesla Roadster
使用别名
导入类时,也可为其指定别名。
要在程序中创建大量电动汽车实例,需要反复输入ElectriCar,非常麻烦。为避免这种烦恼,可在import语句中给ElectriCar指定一个别名:
from electric_car import ElectriCar as EC
my_tesla=EC("tesla","roadster",2019)
print(my_tesla.get_descriptive_name())
输出:
2019 Tesla Roadster
自定义工作流程
-
熟悉Python 提供的选项,这样才能确定哪种组织方式是最佳的,并能理解别人开发的项目。
-
一开始应让代码结构尽可能简单。
-
先尽可能在一个文件中完成所有的工作,确定一起都能正常运行后,再将类移到独立的模块中。
-
如果你喜欢模块和文件的交互方式,可在项目开始时就尝试将类存储到模块中。
-
先找出让你能够编写出可行代码的方式,再尝试改进代码。
Python 标准库
- Python标准库是一组模块,我们安装的Python都包含它。
- 可以使用标准库中的任何函数和类,只需在程序开头包含一条简单的 import 语句即可。
#randint()将两个整数作为参数,并随机返回一个位于这两个整数之间(含)的整数
>>> from random import randint
>>> randint(1,6)
5
#函数choice()将一个列表或者元组作为参数,并随机返回其中的一个元素。
>>> from random import choice
>>> players = ["charles", "martina", "michael", "florence", "eli"]
>>> first_up = choice(players)
>>> first_up
'florence'
类编码风格
-
类名应采用驼峰命名法,即将类名中的每个单词的首字母都大写,而不使用下划线。
-
实例名和模块名都采用小写格式,并在单词之间加上下划线。
-
对应每个类,都应紧跟在类定义后面包含一个可以使用标准库中的任何函数和类,只需在程序开头包含一条简单的 import 语句即可。。这种文档字符串简要地描述类的功能,并遵循编写函数的文档字符串时采用的格式约定。每个模块也都应该包含一个文档字符串,对其中的类可用于做什么进行描述。
-
可使用空行来组织代码,但不要滥用。在类中,可使用一个空行来分割方法,而在模块中,可使用两个空行来分割类。
-
需要同时导入标准库中的模块和你编写的模块时先导入标准库模块的import语句,再添加一个空行,然后编写导入你自己编写的模块的import语句。