面向对象编程是最有效的软件编写方法之一。在面向对象编程中,你编写表示现实世界中的事物和情景的类,并基于这些类来创建对象。编写类时,定义一个大类对象都有的通用行为。基于类创建对象时,每个对象都自动具备这种通用行为,然后可根据需要赋予每个对象独特的个性。根据类来创建对象被称为实例化,这让你能够使用类的示例。
一、创建和使用类
使用类几乎可以模拟任何东西。
1、创建类
dog.py
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."
注:
- 根据约定,首字母大写的名称指的是类,这个类定义中的括号是空的,因为我们要从空白创建这个类。
- 三引号引起来的是文档字符串,对这个类的功能进行描述。
- 方法
__init__()
是一个特殊的方法,每当你根据类创建新的实例时,Python都会自动运行它。开头和结尾各有两个下划线,旨在避免Python默认方法与普通方法发送名称冲突。形参self
必不可少,还必须位于其他形参的前面。 - 以
self
为前缀的变量都可供类中的所有方法使用。
在Python 2.7中国创建类时,应在括号内包含单词object
class ClassName(object):
2、根据类创建实例
class Dog():
--snip--
my_dog = Dog('Bob',2)
print("My dog's name is " + my_dog.name.title() +".")
约定:首字母大写的名称指的是类,小写的名称指的是根据类创建的实例。
① 访问属性(句点表示法)
my_dog.name
② 调用方法(句点表示法)
my_dog.sit()
③ 创建多个实例
my_dog = Dog('Bob',2)
your_dog = Dog('Mei',3)
二、使用类和实例
类编写好之后,你的大部分时间都将花在使用根据类创建的实例上。你需要执行的一个重要任务是修改实例的属性,可以直接修改实例的属性,也也可以编写方法以特定的方式进行修改。
1、给属性指定默认值
class Dog():
"""一次模拟小狗的简单测试"""
def __init__(self,name,age):
"""初始化属性name和age"""
self.name = name
self.age = age
self.color = 'yellow'
def sit(self):
"""模拟小狗被命令时蹲下"""
print(self.name.title() + " is now sitting."
2、修改属性的值
① 直接修改属性的值
要修改属性的值,最简单的方式时通过实例直接访问它,然后修改属性值。
my_dog.color = 'red'
② 通过方法修改属性的值
将要修改的值传递给一个方法,由它在内部进行修改。
class Dog():
--snip--
def update_color(self,color):
"""修改宠物的颜色"""
self.color = 'color'
③ 通过方法对属性的值进行递增
有时候需要将属性值递增特定的量,而不是将其设置为全新的值。
三、继承
编写类时,并非总是要从空白开始。如果你要编写的类是一个现成类的特殊版本,可使用继承。一个类继承另一个类,它将自动获得另一个类的所有属性和方法。原来的类称为父类(超类),新类称为子类。子类继承了其父类的所有属性和方法,同时还可以定义自己的属性和方法。
1、子类的方法 __ init __ ()
class Dog():
--snip--
class BabyDog(Dog):
"""婴儿狗的特殊之处"""
def __init__(self,name,age):
"""初始化父类的属性"""
super().__init__(name,age)
注:
- 创建子类时,父类必须包含在当前文件中,且位于子类的前面。
- 定义子类时,必须在括号内指定父类的名称。方法
__init__()
接受创建父类实例所需的信息。 -
super()
是一个特殊的函数,帮助Python将父类和子类关联起来。
2、python 2.7中的继承
- 定义父类时在括号内指定object;
- 函数
super()
需要两个实参:子类名和对象self。
class Dog(object):
def __init__(self,name,age):
--snip--
class BabyDog(Dog):
def __init__(self,name,age):
super(Dog,self).__init__(name,age)
--snip--
3、给子类定义属性和方法
class Dog():
--snip--
class BabyDog(Dog):
"""婴儿狗的特殊之处"""
def __init__(self,name,age,mother_name):
"""初始化父类的属性,再初始化婴儿狗的特有属性"""
super().__init__(name,age)
self.mother_name = mother_name
def show_mother_name(self,mother_name):
"""打印婴儿狗的麻麻的名字"""
print("This baby dog's name is " + self.name +" and it's mother is "
+ self.mother_name + ".")
4、重写父类的方法
对于父类的方法,只要它不符合子类模拟的实物的行为,都可对其进行重写。为此,可在子类中定义一个与要重写父类方法同名的方法。这样,Python将不会考虑这个父类方法,而只关注你在子类中定义的响应方法。
5、将实例用作属性
使用代码模拟实物时,你可能会发现自己给类添加的细节越来越多:属性和方法清单以及文件都越来越长。在这种情况下,可能需要将类的一部分作为一个独立的类提取出来。可将大型类拆分成多个协同工作的小类。
class Car():
--snip--
class Battery():
--snip-
class ElectricCar(Car):
"""电动汽车的独特之处"""
def __init__(self,make,modle,year):
super().__init__(make,modle,year)
self.battery = Battery()
在ElectricCar
类中,添加了一个名为self.battery
的属性,这行代码让Python创建一个新的Battery
实例,并将该实例存储在属性self.battery
中。每当方法__init__()
被调用时,都将执行该操作。因此现在每个ElectricCar
实例都包含一个自动创建的Battery
实例。
四、导入类
随着你不断地给类添加功能,文件可能变得很长,为此,可将类存储在模块中,然后在主程序中导入所需的模块。
1、导入单个类
dog.py
"""一个可用于表示狗的类"""
class Dog():
--snip--
注:三引号引起来的是模块级文档字符串,对该模块的内容进行简单的描述。
my_dog.py
from dog import Dog
my_dog = Dog('bob',2)
2、在一个模块中存储多个类
虽然同一个模块中的类之间应存在某种相关性,但可根据需要在一个模块中存储任意数量的类。
car.py
"""一组用于表示燃油汽车和电动汽车的类"""
class Car():
--snip--
class Battery():
--snip--
class ElectricCar(Car):
--snip--
3、从一个模块中导入多个类
my_cars.py
from car import Car,ElectricCar
4、导入整个模块
导入整个模块,再使用句点表示法访问需要的类。由于创建类实例的代码都包含模块名,因此不会与当前文件使用的任何名称发生冲突。
import car
my_beetle = car.Car('volkswagen','beetle',2016)
my_tesla = car.ElectricCar('tesla','roadster',2017)
5、导入模块中的所有类
from module_name import *
但是,不推荐使用这种导入方法。
6、在一个模块中导入另一个模块
有时候需要将类分散到多个模块中,一面模块过大,或在同一个模块中国存储不相关的类。此时,可能一个模块的类依赖另一个模块中的类。
electric_car.py
"""一组可用来表示电动汽车的类"""
from car import Car
class ElectricCar(Car):
--snip--
car.py
"""一个可用来表示汽车的类"""
class Car():
--snip--
my_cars.py
from car import Car
from electric_car import ElectricCar
--snip--
五、类编码风格
- 类名应采用驼峰命名法。即将类名中的每个单词的首字母都大写,而不使用下划线。实例名和模块名都采用小写格式,并在单词之间加上下划线。
- 对于每个类,都应紧跟在类定义后面包含一个文档字符串。这种文档字符串简要地描述类的功能,并遵循编写函数的文档字符串时采用的格式约定。每个模块也都应该包含一个文档字符串,对其中的类可用于做什么进行描述。
- 可使用空行来组织代码,但不要滥用。在类中,可使用一个空行来分隔方法;而在模块中,可使用两个空行来分隔类。
- 需要同时导入标准库中的模块和你编写的模块时,先编写导入标准库模块的import语句,再添加一个空行,然后编写导入你自己编写的模块的import语句。