Python基础——Ch14~Ch17本文仅供学习使用
- 14. 命名空间与作用域
- 14.1 命名空间
- 14.1.1 内置命名空间
- 14.1.2 全局命名空间
- 14.1.3 局部命名空间
- 14.1.4 命名空间查找顺序
- 14.2 作用域
- 14.2.1 局部作用域
- 14.2.2 闭包函数外的函数中
- 14.2.3 全局作用域
- 14.2.4 內建作用域
- 14.3 global 和 nonlocal
- 15. 常用内置函数与高阶函数
- 15.1 内置函数
- 15.2 高阶函数
- 16. 递归函数(了解)
- 16.1 兔子问题
- 16.2 最大递归深度限制
- 16.3 解决递归重复计算问题
- 17. 面向对象
- 17.1 模拟学生和老师的一天
- 17.1.1 面向过程(早期语言的编程)
- 17.1.2 面向过程(结构化编程)
- 17.1.3 面向对象(高内聚低耦合)
- 17.2 类、对象、变量、方法
- 17.2.1 类变量、实例变量、实例化
- 17.2.2 类方法、对象方法、静态方法
- 17.2.3 动态定义变量
- 17.3 面向对象三大特性
- 17.3.1 封装
- 17.3.2 继承
- 17.3.3 多态性
14. 命名空间与作用域
思考:下面程序的输出结果是什么?——报错
list = [1, 2, 3, 4, 1, 2, 1, 2] set1 = set(list) print(list(set1))14.1 命名空间
定义:命名空间(Namespace)是一个从名称到对象的映射
实现:大部分命名空间当前由 Python 字典实现(内置命名空间由 builtins
模块实现)
作用:提供了在项目中避免名字冲突的一种方法(各个命名空间是独立
的,没有任何关系,所以一个命名 空间中不能有重名,但不同的命名
空间是可以重名而没有任何影响的)
包含所有 Python 内置对象的名称
在解释器启动时创建,持续到解释器终止
import builtins print(dir(builtins))
dir([object])
不带参数时,返回当前范围内的变量、方法和定义的类型列表
带参数时,返回参数的属性、方法列表
如果参数包含方法__dir__(),该方法将被调用
如果参数不包含__dir__(),该方法将最大限度地收集参数信息
返回的列表按字母表排序(按照 ASCII 码)
print(dir()) print(dir(list))14.1.2 全局命名空间
包含模块中定义的名称,记录了模块的变量、函数、类、其它导入的模块等
在模块被读入时创建,持续到解释器退出
包含函数中定义的名称,记录了函数的变量、参数等
一个函数的局部命名空间在这个函数被调用时创建,持续到函数结束
import random, copy
def func1(arg1, arg2):
num = 666
print("func1-globals:n", globals(), end="nn")
# 返回当前全局命名空间的字典
print("func1-locals:n", locals(), end="nn")
# 返回当前局部命名空间的字典
return num, arg1, arg2
def func2(arg1, arg2):
num = 777
print("func2-globals:n", globals(), end="nn")
# 返回当前全局命名空间的字典
print("func2-locals:n", locals(), end="nn")
# 返回当前局部命名空间的字典
return num, arg1, arg2
num = 111
func1(222, 333)
func2(444, 555)
# 在全局命名空间下,globals()和locals()返回相同的字典
print(globals())
print(locals())
14.1.4 命名空间查找顺序
局部命名空间 -> 全局命名空间 -> 内置命名空间
14.2 作用域一个程序的所有的变量并不是在哪个位置都可以访问的。访问权限决定于这个变量是在哪里赋值的。
变量的作用域决定了在哪一部分程序你可以访问哪个特定的变量名称。
定义:Python 程序可以直接访问命名空间的正文区域
作用:决定了哪一部分区域可以访问哪个特定的名称
分类:(L - E - G - B 作用域依次增大)
局部作用域(Local) - L
闭包函数外的函数中(Enclosing) - E
全局作用域(Global) - G
內建作用域(Built-in) - B
规则:在当前的作用域如果找不到对应名称,则去更大一级的作用域去找,直到最后找不到就会报错
说明:只有模块(module)、类(class)以及函数(def、lambda)才会引入新的作用域
定义在函数内部的变量拥有一个局部作用域,定义在函数外的拥有全局作用域。
全局变量:在模块内、在所有函数外面、在class外面,这就是全局变量
局部变量: 在函数内、在class的方法内(未加self修饰),这就是局部变量
def func(): a = 2 # 局部变量 b = 3 # 局部变量 print(a + b) # 局部作用域可以调用局部变量a,b print(d) # 局部作用域可以调用全局变量d d = 4 # 全局变量 func() # print(a, b) # 全局变量不能调用局部变量14.2.2 闭包函数外的函数中
def outer(): b = 2 # Enclosing变量b,c c = a + 3 # Enclosing可以调用全局变量a def inner(): c = 5 # 局部变量c print(a) # 局部作用域可以调用全局变量a print(b) # 局部作用域可以调用Enclosing变量b print(c) # 优先调用自己作用域内的变量c,而不调用 Enclosing变量c return inner() a = 1 # 全局变量 outer()14.2.3 全局作用域
def outer(): a = c + 2 # Enclosing可以调用全局变量c def inner(): b = c + 3 # 局部作用域可以调用全局变量c print(a + b) # 局部作用域可以调用Enclosing变量a return inner() c = 1 # 全局变量 outer() print(c) # 调用全局变量c14.2.4 內建作用域
# abs是内置函数、int是内置类,它们都在内建作用域builtins模块中 num1 = abs(-100) num2 = int(3.141592653)14.3 global 和 nonlocal
当内部作用域想要给外部作用域的变量重新赋值时,可以用 global 或 nonlocal关键字声明:
对于不可变类型的全局变量来说,要在函数中修改需要global声明
对于可变类型的全局变量来说,要在函数中修改可以不使用global声明
def outer(): global a, b # 声明当前作用域的a,b为全局变量 a, b, c, d = 3, 4, 5, 6 print(a, b) def inner(): global a, b # 声明当前作用域的a,b为全局变量 nonlocal c, d # 声明当前作用域的c,d为Enclosing变量 a, b, c, d = 7, 8, 9, 0 inner() print(c, d) a, b = 1, 2 outer() print(a, b)
易错情况
def outer(): def inner(): """ 解决方案一: 在这里用global声明变量a 或者定义一个 局部变量a """ b = a + 1 # 本身没有错,受到下面这行代码的影响才报错 """ 解决方案二: 把等号左边的a换成其他变量名 """ a = a + 1 # 因为等号左边的a属于局部变量,这种写法会导致该作用域的其他a都被解释器判定为局部变量,所以会报错:局部变量 a在赋值前被引用 print(a, b) return inner a = 1 outer()() def outer(): lis = [1, 2] def inner(): """ 解决方案一: 在这里用nonlocal声明变量lis 或者定义 一个局部变量lis """ res = lis.append(3) # 本身没有错,受到下面这行代码的影响才报错 """ 解决方案二: 把等号左边的lis换成其他变量名 """ lis = lis.append(3) # 因为等号左边的lis属于局部变量,这种写法会导致该作用域的其他lis都被解释器判定为局部变量,所以会报错:局部变量lis在赋值前被引用 print(lis, res) return inner outer()()15. 常用内置函数与高阶函数 15.1 内置函数
eval()
在全局和局部环境中评估给定的source。
source可以是字符串类型的运算表达式,也可以是 compile() 返回的代码对象。全局变量必须是字典,局部变量可以是映射,默认为当前全局变量和局部变量。如果只给出了全局变量,则默认值为全局变量。
eval('1+1')
excel()
在全局和局部环境中执行给定的source。
source可以是字符串类型的python语句,也可以是 compile() 返回的代码对象。全局变量必须是字典,局部变量可以是映射,默认为当前全局变量和局部变量。如果只给出了全局变量,则默认值为全局变量。
exec('print("Python")')
15.2 高阶函数
定义:参数或(和)返回值为其他函数的函数
filter(function, iterable)
func:函数(func 的必需参数只能有一个),也可以为 None
iterables:可迭代对象
将 iterable 中每个元素作为参数传递给函数,根据函数的返回结果进行判断 True 或 False,将判断为 True 的 iterable 中的元素构建新的迭代器并返回
如果 function 为 None,直接判断 iterable 中元素 True 或 False,再返回为 True 的元素构建的新的迭代器
# 思考:如果换成 lambda x: print(x-1) 会怎样? object1 = filter(lambda x: x-1, [1, 2, 3, False, 4]) print(list(object1)) object3 = filter(None, [1, 2, 0, 3, False, 4]) print(list(object3))
map(func, *iterables)
func:函数(func 的必需参数要和 iterables 个数相同)
iterables:可迭代对象
用 iterables 中的每个元素作为函数的参数来调用函数,以迭代器形式返回所有结果
当有多个 iterables 对象时,最短的 iterables 耗尽则函数停止
def square(a): return a**2 # 思考:如果改为 print(a**2) 会怎样? result = map(square, [1, 2, 3]) print(list(result)) result = map(lambda a: a**2, [1, 2, 3]) print(list(result)) result = list(map(float, ["1", "2", "3"])) print(result) # 类似于zip的取元素方式 result = list(map(lambda x, y, z: x+y+z, [1, 2, 3], [3, 2, 1], [1, 3, 2])) print(result) # 当有多个 iterables 对象时,最短的 iterables 耗尽则函数停止 result = list(map(lambda x, y, z: x+y+z, [1, 2, 3], [3, 2, 1], [1, 3])) print(result)
def add(a, b):
return a + b
func = lambda a, b: a + b
print(func(1, 2))
#应用场景:作为其他函数的参数来使用
# map(func, *序列):多个序列的对应元素按照指定的方法进行变换得到新的序列
list1 = [1, 2, 3, 4]
list2 = [5, 6, 7, 8]
# func为一个函数名
result = list(map(add, list1, list2))
print(result)
# func为一个lambda表达式
result = list(map(lambda a, b: a + b, list1, list2))
print(result)
reduce(function, iterable[, initial])
function:函数(function 必需参数只能有两个)
iterable:可迭代对象
initial:初始值
在 Python2 中 reduce() 是内置函数,而在Python3中 reduce() 函数是在functools模块中的,所以在使用的时候需要先导入functools 模块
在没有指定 initial 参数时,先把 iterable 的前两个元素作为参数调用函数,把这次函数的结果以及iterable 的下一个元素又作为参数再调用函数,以此类推
在指定 initial 参数时,先把 initial 值和 iterable 的第一个元素作为参数调用函数,把这次函数的结果以及 iterable 的下一个元素又作为参数再调用函数,以此类推
如果 iterable 为空,返回 initial ,此时如果没有指定 initial,则报错
如果 iterable 只有一个元素且没有指定 initial,返回该元素
from functools import reduce def add(m, n): s = m + n return s # 如果改为 print(s) 会怎样? # 过程:[(1+2)+3]+4 = 10 result = reduce(add, [1, 2, 3, 4]) print(result) # 过程:2*[2*(2*5+1)+2]+3 = 51 result = reduce(lambda x, y: 2*x + y, [1, 2, 3], 5) print(result) # iterable为空,返回initial result = reduce(lambda x, y: 10*x + 2*y, [], 123) print(result) # 123 # iterable只有一个元素且没有指定 initial,返回该元素 result = reduce(lambda x, y: 10*x + 2*y, [123]) print(result) # 123 # 过程:10*2 + 2*123 = 266 result = reduce(lambda x, y: 10*x + 2*y, [123], 2) print(result)
all(*iterate):
对可迭代对象中的每一个元素进行bool运算,如果全为True则返回True,否则返回False
any(*iterate):
对可迭代对象中的每一个元素进行bool运算,如果任一个结果为True则返回True,否则返回False
sorted(iterable, key = None, reversed = False)
list1 = [1, 2, 5, 4]
list2 = [(1, 3), (2, 1), (4, 0)]
print(list(sorted(list2)))
print('reverse=True:')
print(list(sorted(list1, reverse=True)))# reverse=True 是否降序 True表示降序
print(list(sorted(list2, key=lambda x: x[1])))# 如果每个元素有多个值的时候,key指定排序元素的位置
dic_v = [1, 2, 3]
dic_k = ['a', 'c', 'b']
dic = dict(zip(dic_k, dic_v))
print(dic)
print(list(sorted(dic.items())))
print(dict(sorted(dic.items(), key=lambda x: x[1])))
16. 递归函数(了解)
递归函数:递归就是子程序(或函数)直接调用自己或通过一系列调用语句间接调用自己,是一种描述问题和解决问题的基本方法。(一句话,自己调用自己)
定义:程序调用自身的编程技巧称为递归。
思想:将一个大问题分解成一个个的小问题,然后再从小问题回推出大问题
一般来说,递归函数要满足2个条件:
递归边界条件(一般到递归边界则终止当前递归)
递归推理(一般是提取重复的子问题,不断向递归边界靠拢或者不断缩小问题规模)
一般而言,兔子在出生两个月后,就有繁殖能力,一对兔子每个月能生出一对小兔子来。如果所有兔子都不死,那么怎么确定第 n 个月有多少对兔子呢?
# 循环实现 def get_rabbits(m): num1 = 1 num2 = 1 for _ in range(m-1): if m < 2: return 1 num1, num2 = num1+num2, num1 return num1 # 递归实现 def get_rabbits(m): if m < 2: return 1 return get_rabbits(m-1) + get_rabbits(m-2) print(get_rabbits(12))16.2 最大递归深度限制
def get_rabbits(m): if m < 2: return 1 return get_rabbits(m-1) + get_rabbits(m-2) print(get_rabbits(998)) # 不报错(基于老师的电脑) print(get_rabbits(999)) # 报错(基于老师的电脑)
import sys def get_rabbits(m): if m < 2: return 1 return get_rabbits(m-1) + get_rabbits(m-2) print(sys.getrecursionlimit()) # 返回默认的最大递归深度 1000 sys.setrecursionlimit(1500) # 设置最大递归深度为1500 print(get_rabbits(999)) # 放宽最大递归深度之后不报错了16.3 解决递归重复计算问题
当 m 比较大时,比如 m=50,会发现程序计算会变得非常慢,因为递归程序进行了大量的重复计算;要解决递归的重复计算问题,只要把之前已经计算过的数和结果储存起来,后面如果再计算这个数就直接取结果
store={}
def get_rabbits(m):
if m < 2:
return 1
if m in store:
return store[m]
result = get_rabbits(m-1) + get_rabbits(m-2)
store[m] = result
return result
for m in range(1000):
result = get_rabbits(m)
if m == 999:
print(result)
17. 面向对象
编程方式:
函数式:将某功能代码封装到函数中,日后便无需重复编写,仅调用函数即可
面向过程(Procedure Oriented):看名字它是注重过程的。当解决一个问题的时候,面向过程会把事情拆分成: 一个个函数和数据(用于方法的参数)。然后按照一定的顺序,执行完这些方法(每个方法看作一个个过程),等方法执行完了,事情就搞定了。
面向对象(Object Oriented):看名字它是注重对象的。当解决一个问题的时候,面向对象会把事物抽象成对象的概念,就是说这个问题里面有哪些对象,然后给对象赋一些属性和方法,然后让每个对象去执行自己的方法,问题得到解决。
17.1 模拟学生和老师的一天 17.1.1 面向过程(早期语言的编程)count_s = 0
stu1 = "张三"
age_s = 18
adres_s = "黄土高坡"
print(f"大家好! 我是{stu1}, 今年{age_s}岁, 家住在 {adres_s}, 欢迎大家有空来玩哦!")
count_s += 1
print(f"{stu1}起床")
print(f"{stu1}刷牙")
print(f"{stu1}洗脸")
print(f"{stu1}吃菜")
print(f"{stu1}扒饭")
print(f"{stu1}账号登录成功")
print(f"{stu1}看视频")
print(f"{stu1}写代码")
print(f"{stu1}吃菜")
print(f"{stu1}扒饭")
print(f"{stu1}看视频")
print(f"{stu1}写代码")
print(f"{stu1}吃菜")
print(f"{stu1}扒饭")
print(f"{stu1}刷牙")
print(f"{stu1}洗脸")
print(f"{stu1}睡觉")
print(f"当前统计的学生人数是: {count_s} 人")
count_t = 0
teacher1 = "老王"
age_t = 40
adres_t = "人民广场"
print(f"大家好! 我是{teacher1}, 今年{age_t}岁, 家住在 {adres_t}, 欢迎大家有空来玩哦!")
count_t += 1
print(f"{teacher1}起床")
print(f"{teacher1}刷牙")
print(f"{teacher1}洗脸")
print(f"{teacher1}吃菜")
print(f"{teacher1}扒饭")
print(f"{teacher1}今日打卡成功")
print(f"{teacher1}授课")
print(f"{teacher1}答疑")
print(f"{teacher1}写代码")
print(f"{teacher1}吃菜")
print(f"{teacher1}扒饭")
print(f"{teacher1}授课")
print(f"{teacher1}答疑")
print(f"{teacher1}写代码")
print(f"{teacher1}吃菜")
print(f"{teacher1}扒饭")
print(f"{teacher1}刷牙")
print(f"{teacher1}洗脸")
print(f"{teacher1}睡觉")
print(f"当前统计的老师人数是: {count_t} 人")
17.1.2 面向过程(结构化编程)
count_s = 0
stu1 = "张三"
age_s = 18
adres_s = "黄土高坡"
print(f"大家好! 我是{stu1}, 今年{age_s}岁, 家住在 {adres_s}, 欢迎大家有空来玩哦!")
count_s += 1
def wash(name):
print(f"{name}刷牙")
print(f"{name}洗脸")
def eat(name):
print(f"{name}吃菜")
print(f"{name}扒饭")
def study(name):
print(f"{stu1}看视频")
print(f"{stu1}写代码")
print(f"{stu1}起床")
wash(stu1)
eat(stu1)
print(f"{stu1}账号登录成功")
study(stu1)
eat(stu1)
study(stu1)
eat(stu1)
wash(stu1)
print(f"{stu1}睡觉")
print(f"当前统计的学生人数是: {count_s} 人")
count_t = 0
teacher1 = "老王"
age_t = 40
adres_t = "人民广场"
print(f"大家好! 我是{teacher1}, 今年{age_t}岁, 家住在 {adres_t}, 欢迎大家有空来玩哦!")
count_t += 1
def wash(name):
print(f"{name}刷牙")
print(f"{name}洗脸")
def eat(name):
print(f"{name}吃菜")
print(f"{name}扒饭")
def work(name):
print(f"{name}授课")
print(f"{name}答疑")
print(f"{name}写代码")
print(f"{teacher1}起床")
wash(teacher1)
eat(teacher1)
print(f"{teacher1}今日打卡成功")
work(teacher1)
eat(teacher1)
work(teacher1)
eat(teacher1)
wash(teacher1)
print(f"{teacher1}睡觉")
print(f"当前统计的老师人数是: {count_t} 人")
17.1.3 面向对象(高内聚低耦合)
程序=数据+算法。面向过程编程,更侧重于算法;而面向对象编程更侧重于数据。
class Student:
count = 0
def __init__(self, name, age, address):
self.name = name
self.age = age
self.address = address
Student.count += 1
def show_time(self):
print(f"大家好! 我是{self.name}, 今年{self.age} 岁, 家住在{self.address}, 欢迎大家有空来玩哦!")
def get_up(self):
print(f"{self.name}起床")
def wash(self):
print(f"{self.name}刷牙")
print(f"{self.name}洗脸")
def eat(self):
print(f"{self.name}吃菜")
print(f"{self.name}扒饭")
def login_ID(self):
print(f"{self.name}账号登录成功")
def study(self):
print(f"{self.name}看视频")
print(f"{self.name}写代码")
def sleep(self):
print(f"{self.name}睡觉")
def counter(cls):
print(f"当前统计的学生人数是: {cls.count} 人")
stu1 = Student("张三", 18, "黄土高坡")
stu1.show_time()
stu1.get_up()
stu1.wash()
stu1.eat()
stu1.login_ID()
stu1.study()
stu1.eat()
stu1.study()
stu1.eat()
stu1.wash()
stu1.sleep()
stu1.counter()
class Teacher:
count = 0
def __init__(self, name, age, address):
self.name = name
self.age = age
self.address = address
Teacher.count += 1
def show_time(self):
print(f"大家好! 我是{self.name}, 今年{self.age} 岁, 家住在{self.address}, 欢迎大家有空来玩哦!")
def get_up(self):
print(f"{self.name}起床")
def wash(self):
print(f"{self.name}刷牙")
print(f"{self.name}洗脸")
def eat(self):
print(f"{self.name}吃菜")
print(f"{self.name}扒饭")
def clock_in(self):
print(f"{self.name}今日打卡成功")
def work(self):
print(f"{self.name}授课")
print(f"{self.name}答疑")
print(f"{self.name}写代码")
def sleep(self):
print(f"{self.name}睡觉")
def counter(cls):
print(f"当前统计的老师人数是: {cls.count} 人")
teacher1 = Teacher("老王", 40, "人民广场")
teacher1.show_time()
teacher1.get_up()
teacher1.wash()
teacher1.eat()
teacher1.clock_in()
teacher1.work()
teacher1.eat()
teacher1.work()
teacher1.eat()
teacher1.wash()
teacher1.sleep()
teacher1.counter()
class Person:
def __init__(self, name, age, address):
self.name = name
self.age = age
self.address = address
def show_time(self):
print(f"大家好! 我是{self.name}, 今年{self.age} 岁, 家住在{self.address}, 欢迎大家有空来玩哦!")
def get_up(self):
print(f"{self.name}起床")
def wash(self):
print(f"{self.name}刷牙")
print(f"{self.name}洗脸")
def eat(self):
print(f"{self.name}吃菜")
print(f"{self.name}扒饭")
def sleep(self):
print(f"{self.name}睡觉")
class Student(Person):
count = 0
def __init__(self, name, age, address, classes):
super().__init__(name, age, address)
self.classes = classes
Student.count += 1
def login_ID(self):
print(f"{self.name}账号登录成功")
def study(self):
print(f"{self.name}看视频")
print(f"{self.name}写代码")
@classmethod
def counter(cls):
print(f"当前统计的学生人数是: {cls.count} 人")
class Teacher(Person):
count = 0
def __init__(self, name, age, address, department):
super().__init__(name, age, address)
self.department = department
Teacher.count += 1
def clock_in(self):
print(f"{self.name}今日打卡成功")
def work(self):
print(f"{self.name}授课")
print(f"{self.name}答疑")
print(f"{self.name}写代码")
@classmethod
def counter(cls):
print(f"当前统计的老师人数是: {cls.count} 人")
stu1 = Student("张三", 18, "黄土高坡", "高三1班")
Student.counter()
teacher1 = Teacher("老王", 38, "人民广场", "教育部")
teacher2 = Teacher("老翁", 39, "黄浦江", "教育部")
teacher3 = Teacher("老李", 40, "黄浦江", "教导处")
Teacher.counter()
类(Class): 用来描述具有相同的属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法。对象是类的实例。
类变量: 类变量在整个实例化的对象中是公用的。类变量定义在类中且在函数体之外。类变量通常不作为实例变量使用。
数据成员: 类变量或者实例变量, 用于处理类及其实例对象的相关的数据。
方法重写: 如果从父类继承的方法不能满足子类的需求,可以对其进行改写,这个过程叫方法的覆盖(override),也称为方法的重写。
局部变量: 定义在方法中的变量,只作用于当前实例的类。
实例变量: 在类的声明中,属性是用变量来表示的。这种变量就称为实例变量,是在类声明的内部但是在类的其他成员方法之外声明的。
继承: 即一个派生类(derived class)继承基类(base class)的字段和方法。继承也允许把一个派生类的对象作为一个基类对象对待。例如,有这样一个设计:一个Dog类型的对象派生自Animal类,这是模拟"是一个(is-a)"关系。
实例化: 创建一个类的实例,类的具体对象。
方法: 类中定义的函数。
对象: 通过类定义的数据结构实例。对象包括两个数据成员(类变量和实例变量)和方法
17.2 类、对象、变量、方法 17.2.1 类变量、实例变量、实例化# 定义一个类对象,类的名字首字母通常大写
class Student(object): # object 是所有类的基类, 通常省略不写
school = '二中' # 类变量
def __init__(self, name):
self.name = name # 实例变量
"""
对__new__(cls)方法介绍:
构造方法。它会将请求实例化所属的类作为实参传给cls(其他实参传给 __init__),
创建并返回这个类的实例对象, 通常不需要显示的声明该方法, 因为父类的 object
中有定义
对__init__(self)方法介绍:
初始化方法。用来定制实例变量, 返回值只能是None(因为负责返回实例对象的是构造
方法) """
""" 实例化时, 会先调用__new__(cls)方法, 在实例对象被创建之后且返回给调用
者之前, 再调用__init__(self)方法, 把实例对象传给self参数, 做进一步定制"""
stu1 = Student('小明') # 实例化, 得到实例对象stu1
stu2 = Student('小红') # 实例化,得到实例对象stu2
""" 实例变量的调用规则 """
print(stu1.name) # 实例对象stu1调用实例变量
print(stu2.name) # 实例对象stu2调用实例变量
# print(Student.name) # 类对象调用不到实例变量
""" 类变量的调用规则 """
print(Student.school) # 类对象可以直接调用类变量(推荐)
print(stu1.school) # 实例对象stu1也可以调用类变量
print(stu2.school) # 实例对象stu2也可以调用类变量
""" 实例变量是每个实例对象所独有的 """
stu1.name = '小强' # 把实例对象stu1的name变量指向新的值
print(stu1.name) # 实例对象stu1再次调用实例变量, 输出新的值
print(stu2.name) # 而实例对象stu2再次调用实例变量, 并不受到影响
""" 类变量在整个实例对象中是公用的
类对象调用类变量重新赋值, 会影响所有的对象对该类变量的调用 """
Student.school = '一中' # 把Student类的school变量指向新的值
print(Student.school) # 类对象再次调用类变量, 输出新的值
print(stu1.school) # 实例对象stu1再次调用类变量, 也输出新的值
print(stu2.school) # 实例对象stu2再次调用类变量, 也输出新的值
""" 实例对象调用类变量重新赋值, 其实是在动态定义变量, 根本与类变量无关 """
stu1.school = '三中' # 动态定义变量,定义了stu1对象的一个实 例变量
print(stu1.school) # 实例对象stu1调用上一步定义的实例变量
print(Student.school) # 而类变量还是那个类变量
print(stu2.school) # 而类变量还是那个类变量
17.2.2 类方法、对象方法、静态方法
在 Python 中,一般把定义在类中的函数叫方法(method),不在类中的叫函数(function)
方法必须有参数,比如对象方法隐式的接收了 self 参数,类方法隐式的接收了 cls 参数
函数可以没有参数
class Student:
@staticmethod # 静态方法 装饰器
def eat():
print('我要开动了~')
@classmethod # 类方法 装饰器
def sleep(cls): # cls表示当前调用的类对象
print('我要就寝了~')
def init(self, age): # self表示当前调用的实例对象
self.age = age
print(f'我{self.age}岁开始学习')
# self, cls不是关键字,可以换成自己写的其他任意名字代替,调用的时候统一就可以了
# 静态方法:类可以直接调用,实例对象也可以调用,推荐类调用
# 类方法:类可以直接调用,实例对象也可以调用,推荐类调用
# 对象方法:实例对象调用,类不可调用
stu = Student()
Student.eat()
Student.sleep()
stu.eat()
stu.sleep()
# print(stu.age) # 报错(stu没有age变量)
stu.init(7) # 可能会碰到另外一种写法:Student.init(stu, 7)
print(stu.age) # 不报错(上一步调用方法之后, 创建了self.age 变量)
思考:静态方法、类方法有什么区别?
class A: var1 = 123 @classmethod def func1(cls): print(cls.var1) @staticmethod def func2(): print(A.var1) class B(A): var1 = 321 A.func1() A.func2() B.func1() B.func2()17.2.3 动态定义变量
class Student:
def __init__(self, name, age):
self.name = name
self.age = age
def show_info(self):
print(f'名字:{self.name}, 年龄:{self.age}, 地址: {self.address}')
def study1(self):
self.course = "语文"
print(f'今天学习的科目是:{self.course}')
def study2(self):
print(f'今天学习的科目是:{self.course}')
stu1 = Student('张三', 18) # 实例化
stu2 = Student('小明', 28) # 实例化
# stu1.show_info() # 报错(因为stu1没有address变量)
stu1.address = '黄土高坡' # stu1动态定义实例变量
print(stu1.address) # 输出: 黄土高坡
# print(stu2.address) # 报错(因为实例变量是每个实例对象所独有的)
stu1.show_info() # 不报错(因为stu1动态定义了address变量)
# stu2.study2() # 报错(因为stu2没有course变量)
stu2.study1() # 不报错(因为study1方法中定义了course变量)
stu2.study2() # 不报错(因为study1方法执行之后, course变量已经被定义了)
stu2.course = '数学' # 动态定义实例变量
stu2.study1() # study1方法中会把self.course重写赋值
class Worker:
def __init__(self, name):
self.name = name
wk = Worker('李四')
Worker.factory = "江南皮革厂" # 动态定义类变量
print(Worker.factory) # 类对象调用类变量(推荐)
print(wk.factory) # 实例对象也可以调用类变量
# print(Worker.name) # 报错(类对象调用不到实例变量)
Worker.name = '王五' # 动态定义类变量
print(Worker.name) # 类对象调用类变量
print(wk.name) # 实例对象优先调用实例变量name, 而不调用类变量name
17.3 面向对象三大特性
17.3.1 封装
在属性或者方法前面加两个下划线开头, 声明为私有属性或者私有方法
私有属性或者私有方法只能在类的内部调用, 不能在类的外部直接调用
但是可以提供公有方法来访问私有属性, 或者调用私有方法
子类无法继承父类的 私有属性 和 私有方法
class Person:
def __init__(self, name, age):
self.__name = name # 私有属性
if age <= 0:
self.__age = "年龄必须大于0" # 私有属性
else:
self.__age = age # 私有属性
# 利用公有方法访问私有属性
def show_info(self):
print(f"姓名:{self.__name}n年龄:{self.__age}")
# 私有方法
def __sleep(self):
print("我要睡觉了, 晚安!")
# 利用公有方法调用私有方法
def sleep(self):
self.__sleep()
ps = Person("赵六", 26)
# print(ps.__name) # 报错(私有属性不能在类的外部直接访问)
# print(ps.__age) # 同上
ps.show_info()
# ps.__sleep() # 报错(私有方法不能在类的外部直接调用)
ps.sleep()
class Person:
def __init__(self, name):
self.__name = name # 私有属性
def __sleep(self): # 私有方法
print("我要睡觉了, 晚安!")
class Student(Person):
def __init__(self, name):
super().__init__(name)
# 子类无法继承父类的私有属性__name
def show_info(self):
print(f"姓名:{self.__name}")
# 子类无法继承父类的私有方法__sleep
def sleep(self):
self.__sleep()
stu = Student("赵六")
# stu.show_info() # 报错
# stu.sleep() # 报错
17.3.2 继承
所有的类都默认继承 object,只是一般不用写出来
子类继承父类后,会拥有父类中所有的非私有属性和方法
继承的作用:从子类来看,继承可以简化代码;从父类来看,子类是对父类功能的扩充
class A: # class A(object) 每一个类默认继承 class object pass class B(A): # class B(A, object) 每一个类默认继承 class object pass
单继承:
class Person:
state = "China"
def eat(self):
print('吃饭')
def speak(self):
print('说话')
class Student(Person):
def study(self):
print('读书')
class Worker(Person):
def work(self):
print('搬砖')
stu = Student()
print(Student.state) # 子类调用父类的属性
stu.study() # 子类调用自己的方法
stu.eat() # 子类调用父类的方法
stu.speak() # 子类调用父类的方法
wk = Worker()
print(Worker.state)
wk.work() # 子类调用自己的方法
wk.eat() # 子类调用父类的方法
wk.speak() # 子类调用父类的方法
class Animal:
def eat(self):
print('吃东西')
class Cat(Animal):
def catch_mouse(self):
print('抓老鼠')
class Ragdoll(Cat):
def cute(self):
print('卖萌')
c1 = Ragdoll()
c1.cute() # 子类调用自己的方法
c1.catch_mouse() # 子类调用父类的方法
c1.eat() # 子类调用父类的父类的方法
""" 继承时,子类是不会继承父类的私有属性和方法但是,如果子类是在父类中 去调用父类的私有属性和方法,那么是可以的因为私有属性和方法可以被直接的 区域就是封装的那个类的区域""" class Person: def __init__(self): print(self) self.__func() # 可以调用 def __func(self): print(1234) class Student(Person): pass stu1 = Student() stu1.__func() # 调用不到
多重继承:
class Animal:
def eat(self):
print('吃东西')
class Cat:
def catch_mouse(self):
print('抓老鼠')
class Ragdoll(Cat, Animal): # 继承多个父类
def cute(self):
print('卖萌')
c1 = Ragdoll()
c1.cute() # 子类调用自己的方法
c1.catch_mouse() # 子类调用Cat父类的方法
c1.eat() # 子类调用Animal父类的方法
继承顺序:
单继承查找顺序:先找自己的,再去找父类,再去找父类的父类,依此类推
多重继承查找顺序:先找自己的,再去找父类的(父类如果有继承, 要把继承找完为止),多个父类按照从左往右的顺序查找
class A:
a = 1
def pr(self):
print('A')
class B(A):
def pr(self):
print('B')
class C(B):
a = 3
def pr(self):
print('C')
class D(B):
pass
c = C()
c.pr()
print(c.a)
d = D()
d.pr()
print(d.a)
class Biology:
def eat(self):
print("Biology吃东西")
class Animal:
def sleep(self):
print("Animal睡觉")
def eat(self):
print("Animal吃东西")
def cute(self):
print("Animal卖萌")
class Cat(Biology):
def sleep(self):
print("Cat睡觉")
class Ragdoll(Cat, Animal):
pass
rd = Ragdoll()
""" 注意区分: super中调用的情况 rd对象调用eat方法, 先找它自己类中有没有,
再找第一个父类Cat中有没有,再找Cat类的父类有没有(依此类推), 最后才找第二
个父类中有没有, 类推第三个父类... """
rd.eat()
rd.sleep()
rd.cute()
方法重写:
在继承中,当父类方法的功能不能满足需求时,可以在子类重写父类的方法
class Animal:
def __init__(self, food):
self.food = food
def eat(self):
print(f"动物吃{self.food}")
class Cat(Animal):
# 为了实现'猫吃鱼'的功能, 而不是父类的'动物吃鱼', 在子类对 eat方法重写
def eat(self):
print(f"猫吃{self.food}")
c = Cat("鱼") # 实例化, 调用父类的初始化函数
c.eat()
super()
super() 是内置的类, 可以调用指定类的父类(超类)
适用场景:
a. 在子类重写父类方法后,想再使用父类的该方法
b. 在多重继承时,想要调用指定类的属性或方法
class Animal:
def eat(self):
print("吃东西")
class Cat(Animal):
def eat(self):
print("吃鱼")
class Ragdoll(Cat):
def eat(self):
print("喝咖啡")
super().eat() # 调用当前类的父类的eat方法
super(Ragdoll, self).eat() # 调用Ragdoll类的父类的eat方法
super(Cat, self).eat() # 调用Cat类的父类的eat方法
rd = Ragdoll()
rd.eat()
class Biology:
def sleep(self):
print("Biology睡觉")
class Animal:
def sleep(self):
print("Animal睡觉")
class Cat(Biology):
def sleep(self):
print("Cat睡觉")
class Ragdoll(Cat, Animal, Biology):
def sleep(self):
super().sleep() # 调用当前类的父类的sleep方法
super(Ragdoll, self).sleep() # 调用Ragdoll类的父类的sleep方法
super(Animal, self).sleep() # 调用Animal类的右边那个类的sleep方法
""" super(Cat, self).sleep()指定Cat, 在多继承时, 先调用它右边一个类
的sleep方法, 如果找不到, 才考虑Cat类本身的父类的sleep方法, 如果还没
有, 则报错 """
super(Cat, self).sleep()
rd = Ragdoll()
rd.sleep()
继承中的__init__方法:
class A:
def E(self):
print('E方法被调用')
def __init__(self, name):
self.name = name
self.Q()
def Q(self):
print(self.name, 'Q方法被调用')
class B(A):
pass
b = B('张三') # 实例化,调用初始化方法,B没有则调用父类中的初始化方法,初始化方法中调用了Q方法
b.E() # 调用父类的E方法
b.Q() # 调用父类的Q方法
class C(A):
def __init__(self, name):
self.names = name
c = C('赵六') # 实例化, 优先调用C中初始化方法
''' 虽然可以调用父类的Q方法, 但是因为Q方法中的self.name没有定义,
因为A的初始化方法没有被调用, 所以报错解决方案: 先通过c调用一次A的
初始化方法或者把C类中的 self.names改为self.name '''
# c.Q() # 报错
class D(A):
def __init__(self, name):
super(D, self).__init__('李四')
self.name = name
d = D('王五') # 实例化, 先调用D的初始化方法, super方法调用父类的初始化方法, 父类的初始化方法中调用Q方法
d.Q() # 调用父类的Q方法
与继承相关的两个内置函数:
isinstance(object, classinfo) object:实例对象
classinfo:类名、基本类型或者由它们组成的元组
如果 object 是 classinfo 的实例或者是其子类的实例,则返回 True
如果 object 不是给定类型的对象,则返回 False
如果 classinfo 是类型对象元组,那么如果 object 是其中任何一个类型的实例或其子类的实例,就返回 True
如果 classinfo 既不是类型,也不是类型元组或类型元组的元组,则将引发 TypeError 异常
class A: pass class B(A): pass class C(A): pass a = A() b = B() c = C() print(isinstance(a, A)) # True print(type(a) == A) # True print(isinstance(b, A)) # True,考虑继承 print(type(b) == A) # False,type不考虑继承 print(isinstance(c, A)) # True,考虑继承 print(type(c) == A) # False,type不考虑继承 print(isinstance(c, (B, A))) # True,c是A子类的实例
issubclass(class, classinfo)
如果 class 是 classinfo 的子类则返回 True
类会被视作其自身的子类
classinfo 也可以是类对象的元组,只要 class 是其中任何一个类型的子类,就返回 True
class A: pass class B(A): pass class C(A): pass print(issubclass(B, A)) # True print(issubclass(C, A)) # True print(issubclass(A, A)) # True,类会被视作其自身的子类 print(issubclass(C, (B, A))) # True17.3.3 多态性
多态性是指具有不同内容的方法可以使用相同的方法名,则可以用一个方法名调用不同内容的方法
class Apple: def change(self): return '啊~ 我变成了苹果汁!' class Banana: def change(self): return '啊~ 我变成了香蕉汁!' class Mango: def change(self): return '啊~ 我变成了芒果汁!' class Juicer: def work(self, fruit): print(fruit.change()) """ 三个内容不同的change方法使用相同的名字命名, 只要改变change的调用对象, 就可以调用不同内容的方法 """ a = Apple() b = Banana() m = Mango() j = Juicer() j.work(a) j.work(b) j.work(m)


![[深度学习]Part1 Python学习基础Ch14~Ch17——【DeepBlue学习笔记】 [深度学习]Part1 Python学习基础Ch14~Ch17——【DeepBlue学习笔记】](http://www.mshxw.com/aiimages/31/876066.png)
