【Python编程从入门到实践】类(self作用,属性)、继承、导入类模版

第九章.类

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

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

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

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

9.2 创建和使用Dog类 self作用

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

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

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

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


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

在这里插入图片描述


  1. 类内访问属性要使用 self.属性名 ,如self.age
  2. 类内访问其他方法要用 self.方法名
  3. 类外访问实例中的属性(t, age)要使用 实例名.属性名,但不需self,如my_dog.age
  4. 类外调用方法 实例名.方法名(参数) 调用类方法时,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)指的是根据类创建的实例

注意:如果你不需要有类的属性,则可以不写__init__(),创建实例时,直接写my_dog = Dog()

class Dog():
    """一次模拟小狗的简单尝试"""
    def roll_over(self, command):
        """模拟小狗被命令时打滚"""
        print("The command is " + command)

# 每个小狗都是一个独立的实例,有自己的一组属性
my_dog = Dog()  # self会自动传递
print(my_dog.roll_over('sit')) # 会多输出一个none,因为print里面已经是print了

类内访问其他方法 self.方法名
类内方法嵌套定义

class Dog():
    """一次模拟小狗的简单尝试"""
    def roll_over(self, command):
        """模拟小狗被命令时打滚"""
        print("The command is " + command)
    
    def Call(self):
        # 类内访问方法
        self.roll_over('Up and Down')

    def Special_Call(self,c1,c2):
        print("The Special Call command is " + c1)
        # 类内访问方法
        self.roll_over(c2) # 这个方法需要的参数可以由c2传入,也可以直接给出
        self.roll_over('up and down') 

# 每个小狗都是一个独立的实例,有自己的一组属性
my_dog = Dog()  # self会自动传递

my_dog.roll_over('sit')
my_dog.Call()
print()
my_dog.Special_Call('swimming', 'go left')
"""
The command is sit
The command is Up and Down

The Special Call command is swimming
The command is go left
The command is up and down
"""


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

class Classname(object)	
	--snip--

9.2.2 🙋两种类属性:Instance variables vs. Class Variables

2023年2月10日 14:30:29 看到yxc是这么写的,感觉到不解,所以查了查资料
在这里插入图片描述

相关资料:https://pynative.com/python-class-variables/

In Class, attributes can be defined into two parts:

  • Instance variables: If the value of a variable varies from object to object, then such variables are called instance variables.
  • 🕵️Class Variables: A class variable is a variable that is declared inside of class, but outside of any instance method or init() method.

在这里插入图片描述

  • Class Variables可以用实例名or self类名访问!
  • 使用类名修改Class Variables时会影响所有的实例名

代码实战:

class Student:
    # school_name: Class variable
    school_name = 'ABC School '

    # constructor
    def __init__(self, name, roll_no):
        self.name = name  # self.name: Instance variables
        self.roll_no = roll_no
        print(self.school_name)
        print(Student.school_name)

# create Objects
s1 = Student('Emma', 10)
s2 = Student('Jessa', 20)

print('Before')
print(s1.name, s1.roll_no, s1.school_name)
print(s2.name, s2.roll_no, s2.school_name)


# 用类名修改会影响所有实例中的school_name
Student.school_name = 'ZXK School'
print('(1) After')
print(s1.name, s1.roll_no, s1.school_name)
print(s2.name, s2.roll_no, s2.school_name)

# Modify class variable using object reference
s1.school_name = 'PQR School'
print('(2) After')
print(s1.name, s1.roll_no, s1.school_name)
print(s2.name, s2.roll_no, s2.school_name)
ABC School 
ABC School 
ABC School 
ABC School 
Before
Emma 10 ABC School 
Jessa 20 ABC School 
(1) After
Emma 10 ZXK School
Jessa 20 ZXK School
(2) After
Emma 10 PQR School
Jessa 20 ZXK School

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 module import class 替换成特定类的定义以及所有(不在 if…main下的)执行语句

如何理解 Python 中的 if name == main


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

from car import Car  # 导入car.py模块中的Car类和所有不在 if main下的执行语句

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.6.1 导入site-packages模块中的子模块

导入某个对象(包、模块、类或函数)是会把它的所有内容导入的(包括子文件夹)


Python3是从site-packages开始导入模块的,子模块需要用.去进行访问(这个.,其实跟np.array()的.是一个道理)

import matplotlib.pyplot.py as plt matplot是一个很大的模块库,它里面有很多子模块,用.去定位,这里我只需要用pyplot这个子模块

参考资料: [python-模块的分类与导入](https://www.cnblogs.com/herosyuan/p/10064333.html) ## 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

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

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

捡起一束光

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值