【Python编程从入门到实践】类、继承、导入类模版

第九章.类

9.1 面向过程与面向对象编程的区别

面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候一个一个依次调用就可以了

面向对象是把构成问题事务分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描叙某个事物在整个解决问题的步骤中的行为

例:一次选举大会有很多的步骤要进行,甲可能需要先为自己发言;然后乙为自己发言;接着甲为别人投票;乙选择弃票;乙提出质疑…
上面一共列举了5个步骤,我们不关心具体某个步骤,而是关心每个对象(甲、乙)在这次选举大会上的整体行为

9.2 创建和使用Dog类

根据类来创建对象被称为实例化

相对于把一系列的函数和变量封装在一起,用来表示一种事物

属性 = 以self为前缀与这个实例关联起来的变量


  • 属性必须在__init__方法内定义
  • 通过self将变量t和age与整个实例关联起来,成为属性
  • 以self为前缀的变量(属性)都可供类中所有的方法使用
  • 变量tmp不是在__init__方法内以self定义的,所以不是属性,不能被其他方法使用

  1. 类内访问属性要使用self.属性名,如self.age
  2. 类外访问实例中的属性(t, age)要使用实例名.属性名,但不需self,如my_dog.age
  3. 类外调用方法实例名.方法名(参数)调用类方法时,self会自动传递
    my_dog.roll_over('up')

示例:使用Dog类和实例
根据Dog类创建的每个实例都将存储名字和年龄,我们赋予了每条小狗蹲下和打滚的能力

class Dog():
    """一次模拟小狗的简单尝试"""

    def __init__(self, name, age):  # 这里name和age是形参
        """初始化属性t和age"""
        # 属性必须在__init__方法内定义
        self.t = name  # 通过self将变量t和age与整个实例关联起来,成为属性
        self.age = age  # 以self为前缀的变量(属性)都可供类中所有的方法使用

    def sit(self):
        """模拟小狗被命令时蹲下"""
        tmp = " probably"  # 这个变量tmp不是在__init__方法内以self定义的,所以不是属性,不能被其他方法使用
        print(self.t.title() + tmp + " is now sitting")  # 类内访问属性要使用self.t

    def roll_over(self, command):
        """模拟小狗被命令时打滚"""
        print("The command is " + command)
        print(self.t.title() + " rolled over!")


# 每个小狗都是一个独立的实例,有自己的一组属性
my_dog = Dog('willie', 7)  # 根据Dog类来创建新实例
your_dog = Dog('lucy', 3)  # self会自动传递

# 类外访问实例中的属性t, age要使用句点表达式,但不需加self
print("My dog, " + my_dog.t.title() + ", is " + str(my_dog.age) + " years old.")
my_dog.sit()
my_dog.roll_over('up')

print("\nYour dog, " + your_dog.t.title() + ", is " + str(your_dog.age) + " years old.")
your_dog.sit()
your_dog.roll_over('down')

输出

My dog, Willie, is 7 years old.
Willie probably is now sitting
The command is up
Willie rolled over!

Your dog, Lucy, is 3 years old.
Lucy probably is now sitting
The command is down
Lucy rolled over!

方法__init__()
类中的函数称为方法,__init()是一个特殊的方法,每次你根据Dog类创建新实例时,Python都会自动运行它,而且虽然没有显式的return,但是Python会自动返回一个表示这条小狗的实例,一般我们把它存到一个变量中,如my_dog = Dog('willie', 7)

为什么类方法定义中包含self ?
因为Python在调用类方法时自动传入实参self,它是一个指向实例本身的引用(类似C++ this指针),让实例能够访问类中的属性和方法

self.t = name获取存储在形参name中的值,并将其存储在变量t中,然后该变量被关联到当前创建的实例。这样的变量t被称为属性

约定:首字母大写的名称(如:Dog)指的是类,小写的名称(如my_dog)指的是根据类创建的实例


在Python2.7中创建类有细微区别,需在括号内包含单词object

class Classname(object)	
	--snip--

9.3 修改属性的值Car类

说明:类中的每个属性(只能在__init__方法中定义)都必须有初始值,哪个这个值是0或空字符串

有三种不同的方法修改属性的值

  1. 直接通过实例进行修改
    my_car.odometer_reading = 23
  2. 直接通过实例进行修改
    my_car.update_odometer(23)
  3. 通过方法进行递增(增加特定的值)
    my_car.increment_odometer(100)

Car.py

# 2020年1月23日 星期四 14:10:40
class Car():
    def __init__(self, make, model, year):
        """初始化描述汽车的属性"""
        self.make = make
        self.model = model
        self.year = year
        self.odometer_reading = 0  # 每个属性必须初始化

    def read_odometer(self):
        """打印汽车的里程信息"""
        print("This car has " + str(self.odometer_reading) + " miles on it.")

    def update_odometer(self, odometer_reading):
        """更新里程信息"""
        self.odometer_reading = odometer_reading

    def increment_odometer(self, increment):
        """
        增加特定里程
        禁止里程表往回调
        """

        if increment < 0:
            print("You can't roll back an odometer!")
        else:
            self.odometer_reading += increment


my_car = Car('Audi', 'A4', 2016)
my_car.read_odometer()

my_car.odometer_reading = 1200  # 法一:直接通过实例进行修改
my_car.read_odometer()

my_car.update_odometer(1400)  # 法二:直接通过实例进行修改
my_car.read_odometer()

my_car.increment_odometer(100)  # 法三:通过方法增加特定值
my_car.read_odometer()

输出

This car has 0 miles on it.
This car has 1200 miles on it.
This car has 1400 miles on it.
This car has 1500 miles on it.

9.4 继承

9.4.1 子类与父类

如果你要编写的类是另一个现成类的特殊版本,可使用继承

一个类继承另一个类后,它将自动获得另一个类的所有属性和方法;原有的类称为父类(superclass),而新类称为子类

子类继承了其父类所有的属性和方法,同时还可以定义自己的属性和方法

创建子类的实例时,首先需要给其父类的所有属性赋值,为此子类的方法__init__()需要父类施以援手

下面创建一个简单的ElectricCar类版本,它具有Car类的所有功能
核心代码super().__init__(make, model, year)
让子类包含父类Car中的所有属性和方法

super()是一个特殊函数,帮助Python把子类和父类关联起来

class Car():
    def __init__(self, make, model, year):
        """初始化描述汽车的属性"""
        self.make = make
        self.model = model
        self.year = year
        self.odometer_reading = 0  # 每个属性必须初始化

    def read_odometer(self):
        """打印汽车的里程信息"""
        print("This car has " + str(self.odometer_reading) + " miles on it.")

    def update_odometer(self, odometer_reading):
        """更新里程信息"""
        self.odometer_reading = odometer_reading

    def increment_odometer(self, increment):
        """
        增加特定里程
        禁止里程表往回调
        """
        if increment < 0:
            print("You can't roll back an odometer!")
        else:
            self.odometer_reading += increment


# 继承
class ElectricCar(Car):  # 父类名写在括号内
    """电动汽车的独特之处"""

    def __init__(self, make, model, year, color):
        """初始化父类(superclass)Car中的属性"""
        super().__init__(make, model, year)  # 继承核心代码:让子类包含父类Car中的所有属性和方法
        self.license_number = 'A2873L'       # 子类可以定义自己的属性和方法
        self.color = color


my_tesla = ElectricCar('tesla', 'model s', 2016, 'red')
my_tesla.odometer_reading = 22  # 可以使用父类的属性或方法
my_tesla.read_odometer()        # 输出 This car has 22 miles on it.
print("The license number is " + my_tesla.license_number + ", and the color is " + my_tesla.color)
# 输出 The license number is A2873L, and the color is red

继承的一点说明:如果一个属性或方法是任何汽车都有的,而不是电动汽车特有的,就应将其加入到Car类而不是ElectricCar类中。这样,使用Car类的人可以获得相应的功能,而使用ElectricCar类只包含处理电动汽车特有的属性和行为代码

所以上面的代码其实不够好,因为color和license_number所有汽车都有,如果是battery则可以加到子类中


Python2.7中的继承

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--

函数super()需要两个实参:子类名和对象self
另外在Python2.7使用继承时务必在定义父类时在括号内指定object

9.4.2 重写父类方法—去其槽粕

对于父类的方法,只要它不符合子类模拟的实物的行为,都可对其进行重写。只需在子类中定义一个同名的方法,Python只会关心子类中定义的方法,不会考虑同名的父类方法

假如Car类有一个名为fill_gas_tank()的方法,它对电动汽车毫无意义的,因此子类需要重写

class ElectricCar(Car)
	--snip--
	def fill_gas_tank(self):
		"""电动汽车没有油箱"""
		print("This car doesn't need a gas tank!")

使用继承时,可让子类保留从父类那里继承而来的精华,并剔除不需要的糟粕

9.4.3 将实例用作属性

9.5 def greeting(name: str) -> str:含义

函数接受并返回一个字符串,注释像下面这样

def greeting(name: str) -> str:
    return 'Hello ' + name

在函数greeting 中,参数name 预期是 str 类型,并且返回 str 类型

官方文档

9.6 导入模块中的类import

Python允许将类存储在模块中,也是以.py为后缀的文件

导入模块(类或函数)是一种有效的编程方式,它能够让你专注于主程序的高级逻辑

导入类的方式其实和导入函数完全一样,也有三种
car.py存了Car类
则可在my_car.py中

  1. 使用from car import Car导入,这样可以直接使用Car类,就好像它是在这个文件中定义的一样(因为已经显式导入了类名)
  2. 使用import car导入,这种方式会导入car中所有的类,调用时要用car.类名
  3. 使用from car import *导入,这种方式不建议使用

当然在一个模块中也可以导入另一个模块

from car import Car  # 导入car.py模块中的Car类

class Battery():
	--snip--
class ElectricCar(Car):  # 定义Car的子类
	--snip--

示例

主程序是my_car.py,导入的模块是car.py和electric_car.py

主程序my_car.py

#!/usr/bin/env python
# -*- encoding: utf-8 -*-
"""
@Author    :   Wilson79
@Filename  :   my_car.py
@Date      :   2020/1/23 下午06:56 
"""

# 主程序my_car.py 导入模块car.py和electric_car.py

from car import Car
from electric_car import ElectricCar, Battery

my_beetle = Car('volkswagen', 'beetle', 2016)
print(my_beetle.get_descriptive_name())

my_tesla = ElectricCar('tesla', 'roadster', 2016)
print(my_tesla.get_descriptive_name())
my_tesla.battery.describe_battery()

输出

2016 volkswagen beetle
2016 tesla roadster
This car has a 70-KWh battery.

类模块 car.py

#!/usr/bin/env python
# -*- encoding: utf-8 -*-
"""
@Author    :   Wilson79
@Filename  :   car.py  
@Date      :   2020/1/23 下午06:53 
"""

"""一个可用于表示汽车的类"""


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

    def read_odometer(self):
        """打印汽车的里程信息"""
        print("This car has " + str(self.odometer_reading) + " miles on it.")

    def update_odometer(self, odometer_reading):
        """更新里程信息"""
        self.odometer_reading = odometer_reading

    def increment_odometer(self, increment):
        """
        增加特定里程
        禁止里程表往回调
        """
        if increment < 0:
            print("You can't roll back an odometer!")
        else:
            self.odometer_reading += increment

类模块 electric_car.py

#!/usr/bin/env python
# -*- encoding: utf-8 -*-
"""
@Author    :   Wilson79
@Filename  :   electric_car.py  
@Date      :   2020/1/23 下午06:54 
"""

"""一组可用于表示电动汽车的类"""

from car import Car  # ElectricCar类要访问其父类Car,所以要导入Car类


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.")


# ElectricCar继承Car
class ElectricCar(Car):
    """电动汽车的独特之处"""

    def __init__(self, make, model, year):
        """初始化父类中的属性"""
        super().__init__(make, model, year)  # 继承核心代码:让子类包含父类Car中的所有属性和方法
        # 每个ElectricCar实例都会包含一个自动创建的Battery实例
        self.battery = Battery()  # 使用实例作为属性

9.7 Python标准库和类编码风格

Python标准库是一组模块,安装的Python都包含它

我们可以使用标准库中的任何函数和类
只需在程序开头包含一条简单地import语句
在这里插入图片描述
以使用模块collections中的一个类——OrderedDict为例

OrderedDict实例会记录你添加时的键-值对的顺序

from collections import OrderedDict

favorite_languages = OrderedDict()  # 创建一个空的有序字典
favorite_languages['kevin'] = 'python'
favorite_languages['jeny'] = 'java'
favorite_languages['sarah'] = 'c'
favorite_languages['wilson'] = 'c++'

for name, language in favorite_languages.items():
    print(name.title() + "'s favorite language is "
          + language.title() + ".")

类编码风格

  • 类名应采用驼峰命名法,如ElectricCar,不能使用下划线

  • 每个类定义后面包含一个文档字符串,简要地描述类的功能

  • 同时导入标准库中的模块和自己编写的模块时,先编写导入标准库模块的import语句,再用一个空行隔开,再写导入自己的模块的import语句,这样让人更容易明白程序使用了各个模块来自何方

from collections import OrderedDict  # 标准库中模块

from car import Car  # 自己编写的模块
from electric_car import ElectricCar, Battery

写在最后:我的博客主要是对计算机领域所学知识的总结、回顾和思考,把每篇博客写得通俗易懂是我的目标,分享技术和知识是一种快乐 ,非常欢迎大家和我一起交流学习,有任何问题都可以在评论区留言,也期待与您的深入交流(^∀^●)

发布了239 篇原创文章 · 获赞 80 · 访问量 9万+

猜你喜欢

转载自blog.csdn.net/qq_43827595/article/details/104293856