[深度学习]Part1 Python学习基础Ch14~Ch17——【DeepBlue学习笔记】

本文仅供学习使用


14. 命名空间与作用域

思考:下面程序的输出结果是什么?——报错

list = [1, 2, 3, 4, 1, 2, 1, 2] 
set1 = set(list) 
print(list(set1))

14.1 命名空间

定义:命名空间(Namespace)是一个从名称到对象的映射
实现:大部分命名空间当前由 Python 字典实现(内置命名空间由 builtins
模块实现)
作用:提供了在项目中避免名字冲突的一种方法(各个命名空间是独立
的,没有任何关系,所以一个命名 空间中不能有重名,但不同的命名
空间是可以重名而没有任何影响的)

14.1.1 内置命名空间

包含所有 Python 内置对象的名称
在解释器启动时创建,持续到解释器终止

import builtins 
print(dir(builtins))

dir([object])
不带参数时,返回当前范围内的变量、方法和定义的类型列表
带参数时,返回参数的属性、方法列表
如果参数包含方法__dir__(),该方法将被调用
如果参数不包含__dir__(),该方法将最大限度地收集参数信息
返回的列表按字母表排序(按照 ASCII 码)

print(dir()) 
print(dir(list))

14.1.2 全局命名空间

包含模块中定义的名称,记录了模块的变量、函数、类、其它导入的模块等
在模块被读入时创建,持续到解释器退出

14.1.3 局部命名空间

包含函数中定义的名称,记录了函数的变量、参数等
一个函数的局部命名空间在这个函数被调用时创建,持续到函数结束

import random, copy 
def func1(arg1, arg2): 
	num = 666
	print("func1-globals:\n", globals(), end="\n\n") 
	# 返回当前全局命名空间的字典 
	print("func1-locals:\n", locals(), end="\n\n") 
	# 返回当前局部命名空间的字典 
	return num, arg1, arg2 

def func2(arg1, arg2): 
	num = 777 
	print("func2-globals:\n", globals(), end="\n\n") 
	# 返回当前全局命名空间的字典 
	print("func2-locals:\n", locals(), end="\n\n") 
	# 返回当前局部命名空间的字典 
	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修饰),这就是局部变量

14.2.1 局部作用域

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) # 调用全局变量c

14.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个条件:
递归边界条件(一般到递归边界则终止当前递归)
递归推理(一般是提取重复的子问题,不断向递归边界靠拢或者不断缩小问题规模)

16.1 兔子问题

一般而言,兔子在出生两个月后,就有繁殖能力,一对兔子每个月能生出一对小兔子来。如果所有兔子都不死,那么怎么确定第 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 类变量、实例变量、实例化

类是对象的类型,具有相同属性和行为事物的统称。类是抽象的,在使用的时候通常会找到这个类的一个具体存在。
万物皆对象,对象拥有自己的特征和行为。
类是对象的类型,对象是类的实例。类是抽象的概念,而对象是一个你能够摸得着,看得到的实体。二者相辅相成,谁也离不开谁。

类由三个部分构成:

  1. 类的名称:类型
  2. 属性:对象的属性
  3. 方法:对象的方法

类变量: 类变量在整个实例化的对象中是公用的。类变量定义在类中且在函数体之外。类变量通常不作为实例变量使用。如果需要用在函数中使用 类名.类属性对象.属性名
实例变量: 定义在方法中的变量,只作用于当前实例的类,对象.属性名——实例属性

实例方法: 类内部的普通方法

  1. 至少要有一个变量,用来保存实例对象、
  2. 习惯上这个变量命名为self
# 定义一个类对象,类的名字首字母通常大写 
class Student(object): # object 是所有类的基类, 通常省略不写 
	school = '二中' # 类变量 
	def __init__(self, name): # 实例方法(实际上是对object的重写)
		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) # 而类变量还是那个类变量

_ _ init _ _ ()构造方法 和 self
_ _ init _ _ ()是一个特殊的方法属于类的专有方法,被称为类的构造函数或初始化方法,方法的前面和后面都有两个下划线。
魔术方法:在一定机制下回调用

这是为了避免Python默认方法和普通方法发生名称的冲突。每当创建类的实例化对象的时候,_ _ init _ _ ()方法都会默认被运行。作用就是初始化已实例化后的对象。
在方法定义中,第一个参数self是必不可少的。类的方法和普通的函数的区别就是self,self并不是Python的关键字,你完全可以用其他单词取代他,只是按照惯例和标准的规定,推荐使用self

属性
尽量把需要用户传入的属性作为实例属性,而把同类都一样的属性作为类属性。实例属性在每创造一个类是都会初始化一遍,不同的实例的实例属性可能不同,不同实例的类属性都相同

实例属性:
在_ _ init _ _ (self,…)中初始化
内部调用时都需要加上self.
外部调用时用“对象名.属性名”调用
类属性:
在_ _ init _ _ ()里初始化
在内部用classname.类属性名调用
外部既可以用classname.类属性名又可以用instancename.类属性名来调用
私有属性:
双下划线_ _ 开头:外部不可通过“对象名.属性名”来访问或者更改,实际将其转化为了_类名__属性名

17.2.2 类方法、对象方法、静态方法

在 Python 中,一般把定义在类中的函数叫方法(method),不在类中的叫函数(function)
方法必须有参数,比如对象方法隐式的接收了 self 参数,类方法隐式的接收了 cls 参数
函数可以没有参数

静态方法:
通过装饰器 @staticmethod 装饰
不能访问实例属性
参数不能传入self
与类相关但是不依赖类与实例的方法

小结:静态方法:

  1. 应用场景:在类的内部实现的过程中,会有一部分的逻辑实现跟类和对象没有关系,只是某个中间过程,则可以放到静态方法里实现。
  2. 使用装饰器 @staticmethod
  3. 形参不需要额外的变量
  4. 调用的时候:使用类名或者实例对象名.方法名()

类方法:
@classmethod
不能访问实例属性
参数必须传入cls
必须传入cls参数(即代表了此类对象-----区别------self代表实例对象),并且用此来调用类属性:cls.类属性名

小结:类方法:

  1. 应用场景:需要获取类本身的时候
  2. 使用装饰器 @classmethod
  3. 形参需要1个额外的变量,变量名随意,常用的是cls
  4. 调用的时候:使用类名或者实例对象名.方法名()
    静态方法与类方法都可以通过类或者实例来调用。都不能够调用实例属性
    静态方法不需要接收参数,使用类名.类属性
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 BBox:
    x, y, h, w = 100, 80, 20, 30# 类变量
    # 类变量:x, y, h, w,在调用setx之后将更新x,类变量的修改会影响所有的实例
    # 实例变量:y,但是要在调用sety之后才有, 实例变量的修改不影响其他实例
    
    def left_top(self):# self保存的是类的实例对象
        return self.x, self.y
    @staticmethod
    def twosum(a, b):# 静态方法
        return a + b      
    def right_bottom(self):
        x = BBox.twosum(self.x, self.w)
        y = self.twosum(self.y, self.h)# twosum(self.x, self.w)
        return x, y
    def sety(self, newvalue):# self保存的是类BBox的实例对象
        self.y = newvalue
    @classmethod
    def setx(cls, newvalue):# 类方法 cls保存的是类本身BBox
        cls.x = newvalue # BBox.x = newvalue
     
b0 = BBox()
b0.right_bottom()
b1 = BBox()
# print('=========修改之前======')
# print('b0.x, b0.y:', b0.x, b0.y)
# print('x, y:', BBox.x,  BBox.y)
# print('b1.x, b1.y:', b1.x, b1.y)
# # b0.setx(0) # -> setx(BBox, 0)
# b1.setx(0) # -> setx(BBox, 0)
# b0.sety(0) # -> sety(b0, 0)
# print('=========修改之后======')
# print(b0.x, b0.y)
# print(b1.x, b1.y)
# print('x, y:', BBox.x,  BBox.y)

思考:静态方法、类方法有什么区别?

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

@classmethod @staticmethod区别:
@staticmethod不需要表示自身对象的self和自身类的cls参数,就跟使用函数一样。
@classmethod也不需要self参数,但第一个参数需要是表示自身类的cls参数。
@classmethod 是一个函数修饰符,它表示接下来的是一个类方法,而对于平常我们见到的则叫做实例方法。
类方法的第一个参数cls,而实例方法的第一个参数是self,表示该类的一个实例。
普通对象方法至少需要一个self参数,代表类对象实例
类方法有类变量cls传入,从而可以用cls做一些相关的处理。并且有子类继承时,调用该类方法时,传入的类变量cls是子类,而非父类。

针对类的属性的一些方法:
可以使用点实例化对象名+.来访问对象的属性
也可以使用以下函数的方式来访问属性(映射方法)

getattr(obj, name[, default]) : 访问对象的属性
hasattr(obj,name) : 检查是否存在一个属性
setattr(obj,name,value) : 设置一个属性。如果属性不存在,会创建一个新属性
delattr(obj, name) : 删除属性
注意:name需要加单引号,obj为实例化对象名称

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

创建和修改:
方式1: 名字.变量名 = 值 ,如果是第一次则是添加,如果不是就是修改

class Speaker:
    name = 'kiki'
    def __init__(self, n):
        self.name = n

    def speak(self, n):
        self.name = n
        print(f'{self.name} 在演讲。')

Speaker.topic = '环保与安全'  # 添加类变量
print(Speaker.topic)

setattr(Speaker, 'topic', '环保安全')  # 方法2
print(Speaker.topic)

speaker = Speaker('小明')
speaker2 = Speaker('李四')

speaker.name = '张三'
print(speaker.name)
speaker2.topic = '环保'

方式2 :setattr——映射方法

setattr:
将给定对象上的命名属性设置为指定的值。
setattr(x, ‘y’, v) 等价于 x.y = v’ ’

17.2.4 内置类属性

Python内置类属性
_ _ dict _ _ : 类的属性(包含一个字典,由类的属性名:值组成) 实例化类名._ _ dict _ _
_ _ doc _ _ :类的文档字符串 (类名.)实例化类名._ _ doc _ _
_ _ name _ _: 类名,实现方式 类名. _ _ name _ _
_ _ bases _ _ : 类的所有父类构成元素(包含了以个由所有父类组成的元组)

单下划线、双下划线、头尾双下划线说明
_ _ foo _ _ : 定义的是特殊方法,类似 _ _ init _ _ () 之类的。
_ foo: 以单下划线开头的表示的是 protected 类型的变量,即保护类型只能允许其本身与子类进行访问(创建的实例可以访问),不能用于 from module import *
_ _ foo: 双下划线的表示的是私有类型(private)的变量, 只能是允许这个类本身进行
访问了

17.2.5 自定义——函数重写

应用场景:
使用数据集进行模型训练
训练的时候,训练一次(使用全部样本算一次),不用全部的样本,而是分小批次
比如:总样本数=6条,epoch=10,在每个epoch里,分2批获取样本,每个批次的样本是3

# 自定义可迭代对象
# range() enumerate() map() list

class Myiter:
    
    def __iter__(self):# 返回值是一个实例对象  for in 
        return self
    
    
    def __next__(self):# 返回值是迭代能得到的元素  直到无法获取下个元素的时候就抛出异常
        pass
# 实现一个可迭代对象,通过这个对象我们可以获取指定范围之内的数列
# 与range(low, high) 类似的功能
# Myrange(0,1, step=1)  ->[0, 1]
# Myrange(1,4, step=1)  ->[1, 2, 3, 4] 

class MyRange:
    def __init__(self, low, high):
        self.low = low
        self.high = high
        self.source = low # 原始值
        self.itercount = 1
         
    def __iter__(self):# 获取全部元素为调用一次
        print('===========调用iter方法===============')
        print(f'迭代次数为:{self.itercount}')
        self.itercount += 1
        # 重置self.low
        #self.low = self.source
        return self
    def __next__(self):
        print('====调用next方法======')
        self.value = self.low
        if self.low > self.high:
            self.low = self.source
            raise StopIteration
        else:
            self.low += 1
            return self.value

#def Series(low, high):
    #while low < high:
        #low += 1
        #yield low - 1
            
series = MyRange(0, 1)
# series = Series(0, 2)

for i in range(1, 3):
    for num in series:
        print(num)

_ _ lt _ _ (self, other)
(method) _ _ lt _ _ : (self: Self@Employee, other: Any) -> Any
_ _ gt _ _ (self, other)
(method) _ _ gt _ _ : (self: Self@Employee, other: Any) -> Any
_ _ eq _ _ (self,other)
(method) _ _ eq _ _ : (self: Self@Employee, other: Any) -> Any

class Employee():
    def __init__(self, name, age):
        self.name = name
        self.age = age
    def __lt__(self, other):
        print('1')
        return self.age < other.age
    def __gt__(self, other):
        print('2')
        return self.age >  other.age
e1 = Employee('小明', 19)
e2 = Employee('Tom', 18)
print(e1 < e2)  # 1 False  小于比较——> __lt__
print(e1 > e2)  # 2 True   大于比较——> __gt__

_ _ len _ _ (self)
(method) _ _ len _ _ : (self: Self@Guniter) -> Any

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

私有对象属性
为了保护属性不被随意修改和访问,可以将属性定义为私有属性。
如果要让内部属性不被外部访问,可以把属性的名称前加上两个下划线_ _ ,在Python中,实例变量名如果以 _ _ 开头,就变成了一个私有变量(private),只有内部可以访问,外部不能访问

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() # 报错

但是通过: _ 类名 _ _ 属性名,可以对私有属性进行访问(伪权限):

class Speaker:
    # 类变量
    name = 'kiki'
    # 初始化方法
    def __init__(self, n, gender, weight):# 实例方法
        # 创建一个实例对象变量/实例变量
        self.name = n
        self.__weight = weight # 私有变量
        self.gender = gender
    @property
    def weight(self):
        if self.gender == 'male':
            return self.__weight
        else :
            return '不好意思,此信息不公开。'
s0 = Speaker('小明', 'male', 150)
print(s0.gender)

# print(s0.__weight)# 报错,私有属性不能直接访问
print(s0._Speaker__weight) # 使用_类名__属性名 进行私有属性的访问
s0._Speaker__weight = 180
print(s0.weight)

访问限制注意事项

  1. 设置为私有属性不能直接通过对象访问属性,但是可以通过实例化对象名._ 类名_ _ 属性名直接进行访问。但是不建议这样操作.不同版本的Python解释器可能会把_ _ 属性名改成不同的变量名。总的来说就是,Python本身没有任何机制阻止你干坏事,一切全靠自觉。
  2. 通过对象名. _ _ 属性名直接修改私有属性。
    表面上看好像修改了其实并没有,因为Python解释器已经将对象内部的属性名解释成_ 类名 _ _ 属性名。如果在外部修改相当于另外声明一个属性。

能直接修改吗?类属性可以直接修改通过类名. _ 类名 _ _ 属性名=(可通过类方法定义后面说明)

17.3.2 继承

即一个派生类(derived class)继承基类(base class)的字段和方法。
继承也允许把一个派生类的对象作为一个基类对象对待。如:一个 Dog 类型的对象派生自 Animal 类。

所有的类都默认继承 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))) # True

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

17.4 装饰器

在原有函数的功能基础上进行修饰

装饰器是可调用的对象,其参数是另一个函数

@property :装饰器,可以控制变量的访问

class BBox:
    # x = 100; y = 80; h = 20; w = 30
    x, y, h, w = 100, 80, 20, 30# 类变量
    @property
    def left_top(self):
        return self.x, self.y
    @property
    def right_bottom(self):
        return self.x + self.w, self.y + self.h
    @property
    def center(self):
        return self.x + self.w // 2, self.y + self.h // 2
    @property
    def box(self):
        return self.h, self.w
    
bbox = BBox()# self.__init__()
#print(bbox.left_top())
#print(bbox.right_bottom())
#print(bbox.left_top() + bbox.right_bottom() + bbox.center())
print(bbox.left_top)
print(bbox.right_bottom)
print(bbox.left_top + bbox.right_bottom + bbox.center)

装饰器可能会处理被装饰的函数,然后把它返回,或者将其替换成另一个函数或可调用对象,其本质上也是一个闭包

def test():
    print('test is running.')
    return 1

def reg(func):
    print('reg is running')
    return test  #返回的函数test

@reg  # call reg函数,reg函数中的func为mysumlist
def mysumlist():  # mysumlist = 装饰器函数的返回值 = test
    lis = [1, 2, 3]
    print('sum(lis):', sum(lis))
    return sum(lis)

print(mysumlist())  # reg is running  test is running.  1
def test():
    print('test is running.')
    return 1

def reg(func):
    print('reg is running')
    def reg_inner( ):
        print('reg_inner is running.')
    return reg_inner  #返回的函数reg_inner

@reg  # call reg函数,reg函数中的func为mysumlist
def mysumlist():  # mysumlist = 装饰器函数的返回值 = reg_inner
    lis = [1, 2, 3]
    print('sum(lis):', sum(lis))
    return sum(lis)

print(mysumlist())  # reg is running  reg_inner is running.  None

上述两个代码中,mysumlist都没有执行,是因为由于修饰器的作用,mysumlist指向为另一个函数,自身的函数无法执行,此时可在reg函数中对原有代码进行调用:

def test():
    print('test is running.')
    return 1

def reg(func):
    print('reg is running')
    func()  # func = mysumlist
    def reg_inner( ):
        print('reg_inner is running.')
    return reg_inner  #返回的函数reg_inner

@reg  # call reg函数,reg函数中的func为mysumlist
def mysumlist():  # mysumlist = 装饰器函数的返回值 = reg_inner
    lis = [1, 2, 3]
    print('sum(lis):', sum(lis))
    return sum(lis)

print(mysumlist())  # reg is running  sum(lis): 6  reg_inner is running.  None

另一个例子:@reg 的一个等效写法

def test():
    print('test is running.')
    return 1

def reg(func):
    print('reg is running')
    func()  # func = mysum
    def reg_inner( ):
        print('reg_inner is running.')
    return reg_inner  #返回的函数reg_inner

@reg
def mysum():
    total = 0
    for i in range(10):
        total += i
    print('total:', total)
    return total
    
print(mysum())  # reg is running  total: 45  reg_inner is running.  None
def test():
    print('test is running.')
    return 1

def reg(func):
    print('reg is running')
    func()  # func = mysumlist
    def reg_inner():
        print('reg_inner is running.')
    return reg_inner  #返回的函数reg_inner

# @reg  # call reg函数,reg函数中的func为mysumlist
def mysumlist():  # mysumlist = 装饰器函数的返回值 = reg_inner
    lis = [1, 2, 3]
    print('sum(lis):', sum(lis))
    return sum(lis)

# print(mysumlist())  # reg is running  sum(lis): 6  reg_inner is running.  None
mysum = reg(mysumlist)  # reg is running sum(lis): 6 

小结:

  1. 其参数是另一个函数(被装饰的函数),返回值是一个函数
  2. 调用装饰器的方式:可以在被装饰函数的定义的上面使用@装饰器名称 ,
  3. 装饰器的一个关键特性是,它们在被装饰的函数定义之后立即运行。
def test():
    print('test is running.')
    return 1

def reg(func):
    # func自由变量
    print('reg is running')
    def reg_inner(*arg, **dictargs):
        func(*arg, **dictargs)  # 解包
        print('reg_inner is running.')
    return reg_inner

@reg
def mysumlist(lis):# mysumlist=reg_inner
    print('sum(lis):', sum(lis))
    return sum(lis)

@reg
def mysum(num):
    total = 0
    for i in range(num):
        total += i
    print('total:', total)
    return total

print(mysumlist([1, 2, 3]))
print(mysum(num=10))

注意事项:在函数上加装饰器,不管函数本身是否运行,装饰器内部都会运行(装饰器 + 函数)
装饰器的一个关键特性是,它们在被装饰的函数定义之后立即运行。

import time
def time_it(func):
    print('time is running.')
    def inner(*args, **dictargs):
        print('time_inner is running.')
        start = time.time()
        result = func(*args, **dictargs)
        end = time.time()
        print('用时:{}秒'.format(end-start))
        return result
    return inner

def reg(func):
    # func自由变量
    print('reg is running')
    def reg_inner(*arg, **dictargs):
        print('reg_inner is ruuning.')
    return reg_inner

@reg
def mysumlist(lis):# mysumlist=reg_inner
    print('sum(lis):', sum(lis))
    return sum(lis)

@time_it
def mysum(num):
    total = 0
    for i in range(num):
        total += i
    print('total:', total)
    return total

print(mysum(num=10000000))

'''
reg is running

time is running.
time_inner is running.
total: 49999995000000
用时:0.4625725746154785秒
49999995000000
'''
import time
def time_it(func):
    print('time is running.')
    def inner(*args, **dictargs):
        print('time_inner is running.')
        start = time.time()
        result = func(*args, **dictargs)
        end = time.time()
        print('用时:{}秒'.format(end-start))
        return result
    return inner

def reg(func):
    # func自由变量
    print('reg is running')
    def reg_inner(*arg, **dictargs):
        print('reg_inner is ruuning.')
    return reg_inner

# @reg
# def mysumlist(lis):# mysumlist=reg_inner
#     print('sum(lis):', sum(lis))
#     return sum(lis)

@time_it
def mysum(num):
    total = 0
    for i in range(num):
        total += i
    print('total:', total)
    return total

print(mysum(num=10000000))

'''
time is running.
time_inner is running.
total: 49999995000000
用时:0.4625725746154785秒
49999995000000
'''

多层装饰器:由内向外执行

import time
def time_it(func):
    print('time is running.')
    def inner(*args, **dictargs):
        print('time_inner is running.')
        start = time.time()
        result = func(*args, **dictargs)
        end = time.time()
        print('用时:{}秒'.format(end-start))
        return result
    return inner

def reg(func):
    # func自由变量
    print('reg is running')
    def reg_inner(*arg, **dictargs):
        print('reg_inner is ruuning.')
        result = func(*arg, **dictargs)
        return result
    return reg_inner

@time_it  # 2. 再进入time_it,
@reg  # 1. 先进入reg,得到reg函数的返回值
def mysumlist(lis, num):  # 3.mysumlist=reg_inner,后又指向inner。此时再运行的func为之前指向reg_inner的mysumlist
    print('mysumlist is running.')
    return sum(lis*num)


print('mysumlist:', mysumlist(lis=[1, 2], num=1000000))

# 去掉装饰器
# mysumlist = time_it(reg(mysumlist))
# print('mysumlist:', mysumlist(lis=[1, 2], num=1000000))

'''
reg is running
time is running.
time_inner is running.
reg_inner is ruuning.
mysumlist is running.
用时:0.018949270248413086秒
mysumlist: 3000000
'''

缓存装饰器:
LRU(Least Recently Used)把耗时的函数结果保存起来,避免传入相同的参数重复计算。的缓存不会无限储存,一段时间不用,或者数量超出一定限制,旧缓存就会扔掉。
from functools import lru_cache#参数maxsize为最多缓存的次数,如果为None,则无限制,设置为2n时,性能最佳;typed如果设置为True,表示不同参数类型结构分开保存,如(1和1.0)区分开

lru_cache(maxsize, typed):缓存装饰器
有相同的操作(参数相同),可以不用执行函数来获取返回值,而是直接拿缓存数据
from functools import lru_cache

Least-recently-used cache decorator.
如果 maxsize 设置为“None”,则 LRU 功能将被禁用,并且缓存可以无限制地增长。
如果 typed 为 True,则将单独缓存不同类型的参数。例如,f(3.0) 和 f(3) 将被视为具有不同结果的不同调用。
缓存函数的参数必须是可散列

def add(x, y): 
    print("calculating: %s + %s" % (x, y)) 
    s=0
    for i in range(x,int(y)):
        s=s+i
    return s
a = add(1, 20000)
print(add(1, 20000.0))
print(add(1, 20000))

'''
calculating: 1 + 20000
calculating: 1 + 20000.0
199990000
calculating: 1 + 20000
199990000
'''
"""lru_cache(maxsize, typed):缓存装饰器"""
from functools import lru_cache

@lru_cache(maxsize=1,typed=False)  # 若为True,结果与上述结果一致
def add(x, y): 
    print("calculating: %s + %s" % (x, y)) 
    s=0
    for i in range(x,int(y)):
        s=s+i
    return s
a = add(1, 20000)
print(add(1, 20000.0))
print(add(1, 20000))

'''
calculating: 1 + 20000
199990000
199990000
'''

maxsize:插入其他输入的次数

"""lru_cache(maxsize, typed):缓存装饰器"""
from functools import lru_cache

@lru_cache(maxsize=2,typed=False)
def add(x, y): 
    print("calculating: %s + %s" % (x, y)) 
    s=0
    for i in range(x,int(y)):
        s=s+i
    return s
a = add(1, 20000)
print(add(1, 20000.0))
print(add(1, 20000))
print(add(1, 20000))
print(add(1, 20002))
print(add(1, 20000))
print(add(1, 20001))
print(add(1, 20001))
print(add(1, 20000))
print(add(1, 20001))
print(add(1, 20001))
'''
calculating: 1 + 20000
199990000
199990000
199990000
calculating: 1 + 20002
200030001
199990000
calculating: 1 + 20001
200010000
200010000
199990000
200010000
200010000
'''
# 自定义带参数的装饰器 被装饰的函数带参数
# 自定义带参数的装饰器 被装饰的函数带参数
def reg_fac(status=False): # 传参
    print('reg_fac is running.')
    def reg(func):     # 3. 返回一个reg_inner函数 func——>test
        print('reg is running.')
        def reg_inner(*args, **dictargs):
            # result = func(*args, **dictargs)
            return func
        return reg_inner # 4。 test指向 reg_inner 再次执行 返回func
    return reg

@reg_fac(False)  # 1. 执行reg_fac,返回reg,test——>reg
def test():
    print('test is running.')
    return 1
# reg = reg_fac(True)
# test = reg(test)
print(test())  # 2. 此时执行的为  reg(test)

'''
reg_fac is running.
reg is running.
<function test at 0x0000022D5717D280>
'''
# 自定义带参数的装饰器 被装饰的函数带参数
def reg_fac(status=False):
    print('reg_fac is running.')
    def reg(func):
        print('reg is running.')
        def reg_inner(*args, **dictargs):
            result = func(*args, **dictargs)
            return result
        return reg_inner
    return reg
@reg_fac(True)
def test():
    print('test is running.')
    return 1
# reg = reg_fac(True)
# test = reg(test)
print(test())

'''
reg_fac is running.
reg is running.
test is running.
1
'''

18. 高级编程

18.1 性能优化

List, set, dict, tuple 和 deque 性能比较:
在这里插入图片描述

18.2 zip拉链操作

# 同时遍历三个数组
x = [[1], 2, 3, 4, 5]
y = ['a', 'b', 'c', 'd']
z = ['a1', 'b2', 'c3', 'd4', 'e5']
for i in zip(x,y,z):  # 其中一个截止即都截止
    print(i)
    
# 不等长序列遍历
from itertools import zip_longest

x = [1, 2, 3, 4, 5, 6]
y = ['a', 'b', 'c', 'd', 'e']
for i in zip_longest(x,y):  # 最长的截止再截止
    print(i)

18.3 拉平嵌套

li=[[1,2],[3,4],[5,6]]
# li=[[1,2],[3,4],[5,6],1]  # baocuo
from itertools import chain
print (list(chain.from_iterable(li))) 
a, t=[[1,2],[3,4],[5,6]],[]
[t.extend(i) for i in a]
print(t)

li2=[[1,2, [1,2]],[3,4],[5,6]]
print (list(chain.from_iterable(li2)))  # [1, 2, [1, 2], 3, 4, 5, 6]
a, t=[[1,2],[3,4],[5,6]],[]
[t.extend(i) for i in li2]
print(t)  # [1, 2, [1, 2], 3, 4, 5, 6]

18.4 避免过多循环

for x in ['a','b','c']:
    for y in ['d','e','f']:
        for z in ['m','n']:
            print (x,y,z)

from itertools import product
for x,y,z in product(['a','b','c'],['d','e','f'],['m','n']):
    print (x,y,z)

18.5 链式推导

import sys
num=[1,2,3] 
myvec1=[]

for x in num:
    a=[x,x*2]
    myvec1.append(a)
print(myvec1)
print(sys.getsizeof(myvec1))

myvec2=[[x,x*2] for x in num]
print(myvec2)
print(sys.getsizeof(myvec2))
# 返回生成器节省内存
myvec3=([x,x*2] for x in num)
print(myvec3)
print(sys.getsizeof(myvec3))
print(next(myvec3))
print(next(myvec3))
print(next(myvec3))

18.6 偏函数

functools.partial冻结参数

from operator import mul
from functools import partial 
triple1 = partial(mul, 3) 
print(triple1(7))

triple2 = partial(mul, 3, 7) 
print(triple2())

# triple3 = partial(mul, 3) 
# print(triple3())  # baocuo

def mysum(a, b, c):
    print(a)
    print(b)
    print(c)
    return a + b + c

triple4 = partial(mysum, 3, 7) 
print(triple4(1))

# triple5 = partial(mysum, c = 3, a = 7)  # baocuo
# print(triple5(1))

triple6 = partial(mysum, b = 3)  
print(triple6(a = 1, c = 7))

def mysum2(*arg, **dicargs):
    for i in dicargs:
        print(i)
    return a + b + c

triple7 = partial(mysum, b = 3) 
print(triple7(a = 1, c = 7))

18.7 闭包

一个函数定义中引用了函数外定义的变量,并且该函数可以在其定义环境外被执行,这样的一个函数我们称之为闭包。装饰器的本质也是闭包

def outer_func():
    list1 = [] #可变的自由变量
    count = 0 #不可变的自由变量
    def inner_func(name,*ll):# 闭包
        nonlocal count # #不可变的自由变量的声明 #类似于global声明
        list1.append([name,ll])
        count += 1
        print('count:%s,lis1:%s'%(count, list1))
    return inner_func

f0 = outer_func() # func0 = inner_func  # 相当于给inner_func起了别名叫f0
f0('1',0)  # inner_func('1')  # count:1,lis1:[['1', (0,)]]
f0('2',0)  # inner_func('2')  # count:2,lis1:[['1', (0,)], ['2', (0,)]]
f0('3',0)  # inner_func('3')  # count:3,lis1:[['1', (0,)], ['2', (0,)], ['3', (0,)]]
print('==================')
func1 = outer_func()  # func0 = inner_func # 相当于给inner_func起了别名叫func1
func1('func1_0')  # inner_func('func1_0')  # count:1,lis1:[['func1_0', ()]]
func1('func1_1')  # inner_func('func1_1')  # count:2,lis1:[['func1_0', ()], ['func1_1', ()]]
func1('func1_2')  # inner_func('func1_2')  # count:3,lis1:[['func1_0', ()], ['func1_1', ()], ['func1_2', ()]]

print(outer_func()(1))# outer_func() inner_func()
print(outer_func()(2))# outer_func() inner_func()
print(outer_func()(3))# outer_func() inner_func()
print(outer_func()(4))# outer_func() inner_func()
print(outer_func()(5))# outer_func() inner_func()

特点:

  1. 函数内部嵌套了函数
  2. 函数的返回值是嵌套的内部函数

变量的作用域得到延申
如果要在定义域范围之后调用内部函数,那就是即要return内部,还要调用外部,才能调用内部
global声明: 对于不可变的全局变量,如果想要在函数内部修改它,就加一个global
nonlocal声明: 对于不可变的自由变量,如果想要在闭包内部修改它,就要加一个nonlocal

闭包陷阱:

def my_func(*args):
    fs = []
    for i in range(3):
        def func():
            return i * i  # 引用自由变量
        fs.append(func)
    return fs

fs1, fs2, fs3 = my_func()
print(fs1())  # 4
print(fs2())  # 4
print(fs3())  # 4

def my_func(*args):
    fs = []
    for i in range(3):
        def func(i = i):  # 缺省值  默认值
            return i * i
        fs.append(func)
    return fs

fs1, fs2, fs3 = my_func()
print(fs1())  # 0
print(fs2())  # 1
print(fs3())  # 4
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

LiongLoure

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

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

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

打赏作者

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

抵扣说明:

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

余额充值