类(重要)
面向对象编程是最有效的软件编写方法之一。
基于类创建对象时,每个对象都自动具备这种通用行为,然后可根据需要赋予每个对象独特的个性。
根据类来创建对象被称为实例化,这让你能够使用类的实例。
\
- 创建和使用类
- 使用类和实例
- 继承
- 导入类
- Python标准库
- 类编码风格
一、创建和使用类
1. 创建Dog类 class Dog():
根据Dog 类创建的每个实例都将存储名字和年龄。我们赋予了每条小狗蹲下sit() 和打滚roll_over() 的能力:
[python] view plain copy
- class Dog():
- """一次模拟小狗的简单尝试"""
- def __init__(self, name, age):
- """初始化属性name和age"""
- self.name = name
- self.age = age
- def sit(self):
- """模拟小狗被命令时蹲下"""
- print(self.name.title() + " is now sitting.")
- def roll_over(self):
- """模拟小狗被命令时打滚"""
- print(self.name.title() + " rolled over!")
(1)方法__init__()
类中的函数称为方法。__init__()是一个特殊的方法,每当你根据Dog类创建新实例时,Python都会自动运行它。在这个方法的名称中,开头和末尾各有两个下划线,这是一种约定,旨在避免Python默认方法与普通方法发生名称冲突。
我们将方法__init__() 定义成了包含三个形参:self 、name 和age 。在这个方法的定义中,形参self 必不可少,还必须位于其他形参的前面。为何必须在方法定义中包含形参self 呢?因为Python 调用这个__init__() 方法来创建Dog 实例时,将自动传入实参self 。每个与类相关联的方法调用都自动传递实参self ,它是一个指向实例本身的引用,让实例能够访问类中的属性和方法。我们创建Dog 实例时,Python 将调用Dog 类的方法__init__() 。我们将通过实参向Dog() 传递名字和年龄;self 会自动传递,因此我们不需要传递它。每当我们根据Dog 类创建实例时,都只需给最后两个形参(name 和age)提供值。
\
另外两个函数的形参都有前缀self 。以self 为前缀的变量都可供类中的所有方法使用,我们还可以通过类的任何实例来访问这些变量。self.name = name 获取存储在形参name 中的值,并将其存储到变量name 中,然后该变量被关联到当前创建的实例。self.age = age 的作用与此类似。
(2)在Python 2.7 中创建类
在Python 2.7 中创建类时,需要做细微的修改—— 在括号内包含单词object :
[python] view plain copy
- class ClassName(object):
- --snip--
2. 根据类创建实例 my_dog = Dog('willie', 6)
下面来创建一个表示特定小狗的实例:
[python] view plain copy
- class Dog():
- --snip--(与前相同)
- my_dog = Dog('willie', 6)
- print("My dog's name is " + my_dog.name.title() + ".")
- print("My dog is " + str(my_dog.age) + " years old.")
(1)访问属性 my_dog.name
[python] view plain copy
- my_dog.name
创建实例输出有关my_dog 的摘要:
[python] view plain copy
- My dog's name is Willie.
- My dog is 6 years old.
(2)调用方法 my_dog.sit()
[python] view plain copy
- class Dog():
- --snip--
- my_dog = Dog('willie', 6)
- my_dog.sit()
- my_dog.roll_over()
输出:
[python] view plain copy
- Willie is now sitting.
- Willie rolled over!
二、使用类和实例
1. Car类 class Car()
[python] view plain copy
- class Car():
- """一次模拟汽车的简单尝试"""
- def __init__(self, make, model, year):
- """初始化描述汽车的属性"""
- self.make = make
- self.model = model
- self.year = year
- def get_descriptive_name(self):
- """返回整洁的描述性信息"""
- long_name = str(self.year) + ' ' + self.make + ' ' + self.model
- return long_name.title()
- my_new_car = Car('audi', 'a4', 2016)
- print(my_new_car.get_descriptive_name())#2016 Audi A4
2. 给属性指定默认值 my_new_car = Car('audi', 'a4', 2016)
下面来添加一个名为odometer_reading 的属性(里程表) ,其初始值总是为0 。我们还添加了一个名为read_odometer() 的方法,用于读取汽车的里程表:
[python] view plain copy
- 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.")
- my_new_car = Car('audi', 'a4', 2016)
- print(my_new_car.get_descriptive_name())
- my_new_car.read_odometer()
[python] view plain copy
- 2016 Audi A4
- This car has 0 miles on it.
3.修改属性的值
(1)直接修改属性的值
直接将里程表读数设置为23 : my_new_car.odometer_reading = 23
[python] view plain copy
- 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.")
- 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()
--
[python] view plain copy
- 2016 Audi A4
- This car has 23 miles on it.
(2)通过方法修改属性的值
my_new_car.update_odometer(23)
[python] view plain copy
- class Car():
- --snip--
- def update_odometer(self, mileage):
- """将里程表读数设置为指定的值"""
- self.odometer_reading = mileage
- my_new_car = Car('audi', 'a4', 2016)
- print(my_new_car.get_descriptive_name())
- my_new_car.update_odometer(23)
- my_new_car.read_odometer()
[python] view plain copy
- 2016 Audi A4
- This car has 23 miles on it.
(3)通过方法对属性的值进行递增
self.odometer_reading += miles \
**
**
[python] view plain copy
- class Car():
- --snip--
- def update_odometer(self, mileage):
- --snip--
- def increment_odometer(self, miles):
- """将里程表读数增加指定的量"""
- self.odometer_reading += miles
- my_used_car = Car('subaru', 'outback', 2013)
- print(my_used_car.get_descriptive_name())
- my_used_car.update_odometer(23500)
- my_used_car.read_odometer()
- my_used_car.increment_odometer(100)
- my_used_car.read_odometer()
[python] view plain copy
- 2013 Subaru Outback
- This car has 23500 miles on it.
- This car has 23600 miles on it.
三、继承
编写类时,并非总是要从空白开始。如果你要编写的类是另一个现成类的特殊版本,可使用继承 。一个类继承另一个类时,它将自动获得另一个类的所有属性和方法;原有的类称为父类 ,而新类称为子类 。子类继承了其父类的所有属性和方法,同时还可以定义自己的属性和方法。
1. 子类的方法__init__()
下面来创建一个简单的ElectricCar (电动汽车)类版本,它具备Car 类的所有功能:class ElectricCar(Car):
[python] view plain copy
- 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
- class ElectricCar(Car):
- """电动汽车的独特之处"""
- def __init__(self, make, model, year):
- """初始化父类的属性"""
- super().__init__(make, model, year)
- my_tesla = ElectricCar('tesla', 'model s', 2016)
- print(my_tesla.get_descriptive_name())
创建子类时,父类必须包含在当前文件中,且位于子类前面。定义子类时,必须在括号内指定父类的名称。方法__init__()接受创建Car 实例所需的信息。
super()是一个特殊函数,帮助Python将父类和子类关联起来。这行代码让Python调用ElectricCar的父类的方法__init__(),让ElectricCar实例包含父类的所有属性。父类也称为超类(superclass),名称super因此而得名。
除方法__init__() 外,电动汽车没有其他特有的属性和方法。当前,我们只想确认电动汽车具备普通汽车的行为:
[python] view plain copy
- 2016 Tesla Model S
2. Python 2.7 中的继承
在Python 2.7 中,继承语法稍有不同,ElectricCar 类的定义类似于下面这样:
[python] view plain copy
- class Car(object):
- def __init__(self, make, model, year):
- --snip--
- class ElectricCar(Car):
- def __init__(self, make, model, year):
- super(ElectricCar, self).__init__(make, model, year)
- --snip--
3. 给子类定义属性和方法
让一个类继承另一个类后,可添加区分子类和父类所需的新属性和方法。
下面来添加一个电动汽车特有的属性(电瓶),以及一个描述该属性的方法。我们将存储电瓶容量,并编写一个打印电瓶描述的方法:def describe_battery(self):
[python] view plain copy
- class Car():
- --snip--
- class ElectricCar(Car):
- """Represent aspects of a car, specific to electric vehicles."""
- 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.")
- my_tesla = ElectricCar('tesla', 'model s', 2016)
- print(my_tesla.get_descriptive_name())
- my_tesla.describe_battery()
[python] view plain copy
- 2016 Tesla Model S
- This car has a 70-kWh battery.
4.重写父类方法
对于父类的方法,只要它不符合子类模拟的实物的行为,都可对其进行重写。为此,可在子类中定义一个这样的方法,即它与要重写的父类方法同名。这样,Python 将不会考虑这个父类方法,而只关注你在子类中定义的相应方法。假设Car 类有一个名为fill_gas_tank() 的方法,它对全电动汽车来说毫无意义,因此你可能想重写它。下面演示了一种重写方式: print("This car doesn't need a gas tank!")
[python] view plain copy
- def ElectricCar(Car):
- --snip--
- def fill_gas_tank():
- """电动汽车没有油箱"""
- print("This car doesn't need a gas tank!")
5.将实例用作属性
使用代码模拟实物时,你可能会发现自己给类添加的细节越来越多:属性和方法清单以及文件都越来越长。在这种情况下,可能需要将类的一部分作为一个独立的类提取出来。你可以将大型类拆分成多个协同工作的小类。
例如,不断给ElectricCar 类添加细节时,我们可能会发现其中包含很多专门针对汽车电瓶的属性和方法。在这种情况下,我们可将这些属性和方法提取出来,放到另一个名为Battery 的类中,并将一个Battery 实例用作ElectricCar 类的一个属性:class Battery():
[python] view plain copy
- 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()
- 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()
[python] view plain copy
- 2016 Tesla Model S
- This car has a 70-kWh battery.
四、导入类
随着你不断地给类添加功能,文件可能变得很长,即便你妥善地使用了继承亦如此。为遵循Python 的总体理念,应让文件尽可能整洁。为在这方面提供帮助,Python 允许你将类存储在模块中,然后在主程序中导入所需的模块。
1. 导入单个类
下面是模块car.py ,其中只包含Car 类的代码:
[python] view plain copy
- """一个可用于表示汽车的类"""
- 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
下面来创建另一个文件——my_car.py ,在其中导入Car 类并创建其实例:from car import Car
[python] view plain copy
- 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()
[python] view plain copy
- <span style="font-weight:normal;">2016 Audi A4
- This car has 23 miles on it.</span>
2. 在一个模块中存储多个类
类Battery 和ElectricCar 都可帮助模拟汽车,因此下面将它们都加入模块car.py 中:
[python] view plain copy
- """一组用于表示燃油汽车和电动汽车的类"""
- class Car():
- --snip--
- class Battery():
- """一次模拟电动汽车电瓶的简单尝试"""
- def __init__(self, battery_size=60):
- """初始化电瓶的属性"""
- self.battery_size = battery_size
- def describe_battery(self):
- """打印一条描述电瓶容量的消息"""
- print("This car has a " + str(self.battery_size) + "-kWh battery.")
- def get_range(self):
- """打印一条描述电瓶续航里程的消息"""
- if self.battery_size == 70:
- range = 240
- elif self.battery_size == 85:
- range = 270
- message = "This car can go approximately " + str(range)
- message += " miles on a full charge."
- print(message)
- class ElectricCar(Car):
- """模拟电动汽车的独特之处"""
- def __init__(self, make, model, year):
- """
- 初始化父类的属性,再初始化电动汽车特有的属性
- """
- super().__init__(make, model, year)
- self.battery = Battery()
现在,可以新建一个名为my_electric_car.py 的文件,导入ElectricCar 类,并创建一辆电动汽车了:
from car import ElectricCar \
[python] view plain copy
- from car import ElectricCar
- my_tesla = ElectricCar('tesla', 'model s', 2016)
- print(my_tesla.get_descriptive_name())
- my_tesla.battery.describe_battery()
- my_tesla.battery.get_range()
[python] view plain copy
- 2016 Tesla Model S
- This car has a 70-kWh battery.
- This car can go approximately 240 miles on a full charge.
3.从一个模块中导入多个类 from car import Car, ElectricCar
[python] view plain copy
- from car import Car, ElectricCar
- my_beetle = Car('volkswagen', 'beetle', 2016)
- print(my_beetle.get_descriptive_name())
- my_tesla = ElectricCar('tesla', 'roadster', 2016)
- print(my_tesla.get_descriptive_name())
[python] view plain copy
- 2016 Volkswagen Beetle
- 2016 Tesla Roadster
4. 导入整个模块 import Car
[python] view plain copy
- 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()</span>
5.导入模块中的所有类
from module_name import *
五、Python 标准库
Python标准库是一组模块,安装的Python都包含它。可使用标准库中的任何函数和类,为此只需在程序开头包含一条简单的import语句。下面来看模块collections中的一个类 ——OrderedDict。
字典让你能够将信息关联起来,但它们不记录你添加键—值对的顺序。要创建字典并记录其中的键—值对的添加顺序,可使用模块collections中的OrderedDict类。OrderedDict实例的行为几乎与字典相同,区别只在于记录了键—值对的添加顺序。
[python] view plain copy
- from collections import OrderedDict
- favorite_languages = OrderedDict()
- favorite_languages['jen'] = 'python'
- favorite_languages['sarah'] = 'c'
- favorite_languages['edward'] = 'ruby'
- favorite_languages['phil'] = 'python'
- for name, language in favorite_languages.items():
- print(name.title() + "'s favorite language is " + language.title() + ".")
注意,这里没有使用花括号,而是调用OrderedDict() 来创建一个空的有序字典,并将其存储在favorite_languages 中。
[python] view plain copy
- Jen's favorite language is Python.
- Sarah's favorite language is C.
- Edward's favorite language is Ruby.
- Phil's favorite language is Python.
六、类编码风格
1.类名应采用驼峰命名法 ,即将类名中的每个单词的首字母都大写,而不使用下划线。
2.实例名和模块名都采用小写格式,并在单词之间加上下划线。
3.对于每个类,都应紧跟在类定义后面包含一个文档字符串。这种文档字符串简要地描述类的功能,并遵循编写函数的文档字符串时采用的格式约定。
4.需要同时导入标准库中的模块和你编写的模块时,先编写导入标准库模块的import 语句,再添加一个空行,然后编写导入你自己编写的模块的import 语句。在包含多条import 语句的程序中,这种做法让人更容易明白程序使用的各个模块都来自何方。