文章目录提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
- 前言
- 1,魔法函数
- 分类
- __repr__
- __str__
- __len__
- 2,深入类和对象
- 2.1,鸭子类型和多态
- 概念
- 抽象基类(abc模块)
- python中的抽象基类不能实例化,
- 为什么Python中有鸭子类型却还有抽象基类
- 实现
- 注意
- 3,instance和type
- is和==
- 4,类变量和实例变量
- 5,类属性和实例属性
- 深度优先算法与广度优先算法与C3算法
- C3算法:
- 6,类方法和实例方法
- 7,数据封装和私有属性
- 8,自省
- 反射
- 9,super
- 10, mixin的继承案例
- 模式特点
- 优点
- 11,Python中的上下文管理器
- 概念
- 更简单的上下文管理器
- 12,自定义序列类
- 序列类型的特点
- 序列类型的分类
- 序列列类型的abc继承关系
- 序列的+,+=和extends的区别
- 切片
- bisect
- 什么时候我们不使用列表
- 几个推导式
- 列表推导式
- 生成器表达式
- 字典推导式
- 13,Dict和Set
- Mapping协议
- 14,引用,垃圾回收
- python中的变量
- is 和 ==符合的区别
- del和垃圾回收
- 一个经典错误
- 15,迭代器,生成器,装饰器
- 迭代器
- 生成器
- 为什么要用生成器
- 装饰器
- 16,一些小技巧
- 17,面向对象
- 概念
- 面向过程和面向对象
- 面向对象三大特性
- 封装
- 继承
- 多态
- 为什么python中没有函数重载
- 18,可变对象和不可变对象
- 分类
- 当我们修改对象的值时
- 函数的参数传递
- 19,深浅拷贝
- 赋值操作
- 20,*args 和 **kwargs
- 21,面向切面编程AOP
- AOP
- 22,新式类和旧式类
- 23,read,readline和readlines
- 24,函数式编程
- 命令式编程
- 声明式编程
- 函数式编程
- 25,闭包
- 为什么要使用闭包
- 总结
前言
python进阶总结
Python基于协议编程
数学运算和非数学运算
- 与对象毫无关联,可以写到任何位置
- 魔法函数定义后,我们不需要调用,python自己会进行调用
开发模式下使用的,输入对象时使用
str字符串格式
len对内置类型使用时,直接读取内置类型内存储的长度
对于其他类型,使用__len__的方法进行变量
当一只鸟走起来像鸭子,游泳起来像鸭子,叫起来也像鸭子,那么这只鸟就可以被称为鸭子
概念与静态语言不同,我们使用鸭子类型,只需要关注它是否有我们需要的方法,是什么类型的对象都不重要
就比如列表中使用extend(),只要目标对象实现了__getitems__,我们即可添加到列表中
鸭子类型和魔法函数构成Python动态语言的基础
抽象基类(abc模块)- python中的抽象基类不能实例化,
- 我们在设计抽象基类时,为其设置一些方法,当我们基础该基类时,我们要覆盖这些方法
我们需要强制某个子类必须实现某些方法:
- 事先定义好约定,用户实现抽象类的子类,用户替换时非常方便·
import abc class CacheBase(metaclass=abc.ABCMeta): @abc.abstractmethod def get(self,key): pass @abc.abstractmethod def set(self,key): pass注意
不经常使用抽象基类
- 其最大用处是帮组我们理解python。
- 抽象基类在设计时,很容易抽象过度,而不容易取理解它。
instance 内部会检测基础类
type 显示类型
- is判断是否为一个对象
- ==判断值是否相等
尽量使用instance,type会有很多误差
class A:
pass
class B(A):
pass
isinstance(A(), A) # returns True
type(A()) == A # returns True
isinstance(B(), A) # returns True
type(B()) == A # returns False
4,类变量和实例变量
- 在类中定义的变量是类变量,属于类
- 实例化类时,初始化的值为实例变量
- 当我们查询实例中变量时,会优先在类中查询,再在类中查询
class A:
aa = 1
def __init__(self,x,y):
self.x = x
self.y = y
a = A(2,3)
A.aa = 11
print(A.aa)
print(a.aa)
a.aa = 12312
# 此刻会为a实例一个aa属性,跟A的属性无关了
print(A.aa)
print(a.aa)
5,类属性和实例属性
- 定义在类中的变量或是方法是类属性
- 实例化后实例中的属性和方法是实例属性
深度:
先一条线不断进行查找
广度:
一层一层走
- 先将基础的所有节点的顺序合并为列表
- 重点在merge函数
公式
mro(E) = [E]+merge(mro(A)+mro(B))
merge 详细过程
- 先判断一个数组的第一个元素是否是不在其他数组中,或是只在其他数组的第一位,那么我们就将其移到左边的列表中,将该元素删除
- 继续向下判断直到右侧为空
供类使用,假如说
- 静态方法就是一个普通的方法,与类和对象无关。
- 类方法属于类,被该类创建的各个对象所共享.
- 实例方法是属于类创建的某个对象的。
实例方法最大的特点在于,它至少也要包含一个self参数,用于绑定调用此方法的实例对象(Python会自动完成绑定)
类方法是会自动把类对象传递进去cls
静态方法无法调动任何的类属性和方法
在实际编程中,几乎不会用到类方法和静态方法,因为我们完全可以使用函数代替它们实现想要的功能,但在一些特殊的场景中(例如工厂模式中),使用类方法和静态方法也是很不错的选择。
- 变量名前,加_就可以称为私有属性。约定俗成,表示我不希望你直接调用
- __前置双下划线,私有化属性或方法,无法在外部直接访问(名字重整所以访问不到,只能是允许这个类本身进行访问了。连子类也不可以)
● foo:一种约定,Python内部的名字,用来区别其他用户自定义的命名,以防冲突.
● _foo:一种约定,用来指定变量私有.程序员用来指定私有变量的一种方式.
● __foo:这个有真正的意义:解析器用_classname__foo来代替这个名字,以区别和其他类相同的命名.
自省就是面向对象的语言所写的程序在运行时,能够知道对象的类型。简单一句就是,运行时能够获知对象的类型。
列出对象的属性
dict
列出对象的所有属性
dir(user) 它返回传递给它的任何对象的属性名称经过排序的列表。
type() type() 函数有助于我们确定对象是字符串还是整数,或是其它类型的对象
hasattr() 对象拥有属性,并且 dir() 函数会返回这些属性的列表。
isinstance() isinstance() 函数测试对象,以确定它是否是某个特定类型或定制类的实例:
自省就是面向对象的语言所写的程序在运行时,所能知道对象的类型.简单一句就是运行时能够获得对象的类型.
● 自省是获取对象的能力
● 反射是操纵对象的能力
先说正射:
正常情况下是导入模块,找到想要的类,再创建实例对象,接着再调用方法,这个方法太麻烦了,一个程序不停的运行,所需要的功能不断增多,不可能手动去一一导入,而且还需要程序去进行判断某个模块功能是否变动,这种情况必须只能是使用反射了。
● 反射就是以字符串的形式去导入模块,通过字符串的形式,去模块中寻找指定函数,并执行。利用字符串的形式去对象中操作成员,一种基于字符串的事件驱动
● 就是把一个对象类型拆解。可以使用字符串的形式去访问和修改对象。
● 应用场景:当我们动态输入一个模块名的就可以使用反射
● 通过hasattr,getattr,delattr,setattr等四 个函数来操作
反射的优点
● 能够用字符串的形式去操作对象,提高了程序的灵活性和扩展性。
● 降低耦合性
● 这种形式可以应对任何类的对象
super并不是调用父类的属性,而是调用mro中下一个类的构造函数
class A:
def __init__(self):
print("A")
class E:
def __init__(self):
print("E")
class B(A):
def __init__(self):https://blog.csdn.net/lxqArticle/article/details/125270911
print("B")
super(B, self).__init__()
class C(E):
def __init__(self):
print("C")
super(C, self).__init__()
class D(B,C):
def __init__(self):
print("D")
super(D, self).__init__()
print(D.__mro__)
d = D()
结果
(, , , , , )
D
B
A
10, mixin的继承案例
混合模式
我们写一个mixin类,这个mixin中的每个类功能很单一
- 功能单一
- 不是我们真正的类,只是一些接口而已
- 不和基类关联,可以和任何基类组合,基类可以不和mixin关联就能初始化成功
- 在mixin中不要使用super
- 不成文规范,mixin类以mixin结尾
避免设计多层次的复杂的继承关系,混入类是为代码重用而生,使得代码结构简单清晰
11,Python中的上下文管理器 概念始于try except finally
上下文管理器协议
- 我们定义一个类,它需要实现__enter__和__exit__俩个魔法函数
- 进入时,进入__enter__获取资源,出去时调用__exit__释放资源
- 利用with语句来使用该类
::: 协议与魔法函数挂钩
更简单的上下文管理器import contextlib
'''
用装饰器转换为上下文管理器
enter内容放于yield之前
exit内容放于yield之后
'''
@contextlib.contextmanager
#它修饰的必须是生成器
def file_open(file_name):
print("file open")
yield 1
print("file end ")
with file_open("bobby.txt") as f_opened:
print("sadasdasd")
12,自定义序列类
序列类型的特点
- 序列类型可以基于for循环遍历
- 容器序列:list,tuple,deque
- 扁平序列:str,bytes,bytearray,array.array
- 可变序列:list,deque,bytearray,array
- 不可变: str,tuple,bytes
跟容器类的抽象基类都放于abc中
- len
- getitem
- setitem
- delitem
- iter
- +是将俩个序列连接,是将一个值给其他数组
- +=就地加, 不需要将内容给其他数组,而且后面可以相加任意序列类型
- extend后面只要是iter就可以添加,没有返回值
关于append和extend - append 直接会把值放到列表中,就是将列表当成一个整体放到该列表中
alist[len(alist):] = [9] #在尾部增加元素 alist[:0] = [1,2] # 在列表头部插入元素 alist[3:3] = [4] # 在列表中间指定位置插入元素 alist[3:] = [4,5,6] # 将三到最后位置变成四五六 alist[::3] = [0]*3 长度必须相等
如何新建一个可以切片的对象
import numbers
class Group:
def __init__(self, group_name, company_name, staffs):
self.group_name = group_name
self.company_name = company_name
self.staffs = staffs
def __reversed__(self):
self.staffs.reverse()
# 对序列进行切片操作
def __getitem__(self, item):
cls = type(self)
if isinstance(item,slice):
return cls(group_name=self.group_name,company_name=self.company_name,staffs=self.staffs)
elif isinstance(item,numbers.Integral)
return cls(group_name=self.group_name,company_name=self.company_name,staffs=[self.staffs[item]])
return self.staffs[item]
def __len__(self):
return len(self.staffs)
def __iter__(self):
return iter(self.staffs)
def __contains__(self, item):
if item in self.staffs:
return True
else:
return False
staffs = ["bobby1","immoc","bobby2"]
group = Group(company_name="imooc",group_name="user",staffs=staffs)
a = group[::2 ]
print(a)
a = group[0]
print(a)
bisect
用来处理已经排序的序列(维护已排序序列)
insort 插入并排序
bisect 查找并插入
bisect库是python中针对有序列表的一个模块,接收已排序列表作为参数。
一.函数介绍
————1 2 查询
-
bisect.bisect(a,x)(默认等同于bisect.bisect_right())
参数:
a——已排序的列表
x——要插入的元素
返回值:
返回x在a中会被顺序插入的位置。若a中已有一个或多个x,返回的位置在最后一个x之后。 -
bisect.bisect_left(a,x)
参数:
a——已排序的列表
x——要插入的元素
返回值:
返回x在a中会被顺序插入的位置。若a中已有一个或多个x,返回的位置在第一个x之前。
————3 4 5 插入
-
bisect.insort(a,x)
参数:
a——已排序的列表
x——要插入的元素
返回值:
None
功能:
在a中上述对应函数会返回的位置插入x。 -
bisect.insort_right(a,x)
参数:
a——已排序的列表
x——要插入的元素
返回值:
None -
bisect.insort_left(a,x)
参数:
a——已排序的列表
x——要插入的元素
- array是C语言的数组
- array只能存放指定类型数据类型
- array的效率很高
- append()
通过一行代码生产列表
odd_list == [i for i in range(21) if i % 2 == 1 ]
- 逻辑复杂时,可以放函数
- 逻辑太复杂就可以不使用列表生成式
- 列表生成式性能高于列表操作
如果列表推到式的中括号变成小括号
字典推导式dict = {value:key for key,value in my_dict.items()}
集合推导式set
set = {key for key,value in my_dict.items()}
13,Dict和Set
先看collections中的abc
dict的子类:
Set和frzenset 无序不重复
Set 集合
frzenset 不可变集合
实现原理:
- dict的查找性能远远大于list
- 底层结构hash表
变量就是对内存地址的引用,内存地址中存放数据
is 和 ==符合的区别is是对地址的引用,==是对内容的判断
del和垃圾回收python的垃圾回收机制是采用引用计数
del 一个对象时,实际是将该引用删除,同时将内存地址中的地址减一
定义类时,尽量不要传递list,如果不传递参数时,会使用默认的类参数地址,可能下次实例化进行的修改会影响到其
15,迭代器,生成器,装饰器 迭代器迭代,是重复反馈过程的活动,通常是为了逼近所需目标或结果。每对过程重复一次称为一次“迭代”,而每次迭代得到的结果会作为下一次迭代的初始值。在 Python 中,迭代是访问集合型数据的一种方式,对于字符串、列表、元组、集合和字典,都可以使用迭代来遍历其中的每个元素,而这些可以使用 for 循环遍历的对象也被称为可迭代对象。
可迭代对象:
可以利用迭代器逐个获取值
要实现的魔法函数
- next函数
- iter函数
迭代器实例
# 迭代器例子
class Fibs:
def __init__(self):
self.a = 0
self.b = 1
def __next__(self):
self.a, self.b = self.b, self.b + self.a
return self.a
def __iter__(self):
return self
fibs = Fibs()
for f in fibs:
if f > 1000:
print(f)
break
# 对可迭代对象利用iter生成一个可迭代对象
it = iter([1, 2, 3])
print(next(it))
print(next(it))
#利用迭代器生成序列
class TestIterator:
value = 0
def __next__(self):
self.value += 1
if self.value > 10: raise StopIteration
return self.value
def __iter__(self):
return self
ti = TestIterator()
print(list(ti))
生成器
生成器是迭代器的子集,在Python中一个函数可以用yiled替代return返回值,这样的话这个函数就变成了一个生成器对象
● yield
● yield from
加上可迭代对象,这样可以把可迭代对象变成生成器
考虑到如果我们使用其他可迭代对象处理庞大的数据时,当创建或者返回值时会申请用于存储整个可迭代对象的内存,显然这是非常浪费的,因为有的元素当前我们用不到,也不会去访问,但它却一直占用这内存。 所以我们可以用到生成器,它不是一次性把所有的结果都返回,而是当我们每读取一次,它会返回一个结果,当我们不读取时,它就是一个生成器表达式,几乎不占用内存。
生成器的函数和迭代器
nested = [[1, 2], [3, 4], [5]]
# 存储每个值,利用next函数进行迭代
def flatten(nested):
for sublist in nested:
for element in sublist:
yield element
print(list(flatten(nested)))
# 生成器推导式
g = ((i+2)**2 for i in range(2,27))
print(next(g))
print(next(g))
print(next(g))
print(next(g))
g = sum(i ** 2 for i in range(10))
print(g)
def flatten(nested):
try:
for sublist in nested:
for element in flatten(sublist):
yield element
except TypeError:
yield nested
a = list(flatten([[[1],2],3,4,[5,[6,7]],8]))
print(a)
# 存在问题 如果里面是字符串就会出现错误所以开始前先进行检测
# 修改代码为
def flatten(nested):
try:
try:
nested + ''
except TypeError:pass
else:raise TypeError
for sublist in nested:
for element in flatten(sublist):
yield element
except TypeError:
yield nested
b = list(flatten(['foo', ['bar',['baz']]]))
print(b)
装饰器
在不改变原函数调用以及内部代码的情况下,为期添加新的功能
def sleep(fun):
def begin():
fun()
print("你看到我了")
return begin
@sleep#装饰器
def eat():
print("你吃饭了?")
eat()
16,一些小技巧
1,
import sys
这句语句告诉Python,我们想要使用这个模块。sys模块包含了与Python解释器和它的环境有关的函数
2,
range(start,end,step)
给定一个范围相当于for(i=0;i<=10;i++)
>>> range(0, -10, -1) # 负数 [0, -1, -2, -3, -4, -5, -6, -7, -8, -9]
3,
A[i], A[min_idx] = A[min_idx], A[i]
可以这样交换列表元素
不同的对象总是有着不同的特征,同一类的对象总是有着相似或者相同的特征
面向过程和面向对象- 面向过程是一种以事件为中心的编程思想,编程的时候把解决问题的步骤分析出来,并且按步骤实现。
- 面向对象就像洗衣机,将一些功能封装给一个对象,我们直接调用该对象的方法即可实现,就像直接点击洗衣就可以实现洗衣
把客观事务封装成抽象的类,隐藏实现细节,仅仅对外公开一个可以调用的接口
继承子类可以实现父类的所有功能,并修改其细节。子类会有父类的一些特点
多态同一事物在不同形态下的表现形式不同
为什么python中没有函数重载- 首先函数重载是为了解决可变参数类型和可变参数各数
- 对于可变参数类型:因为Python中的函数可以接收任意类型的参数
- 对于可变参数个数:Python中有缺省参数,将那些缺少的参数设置为缺省参数即可解决问题
- 所以python中不需要函数重载
Python中可变对象和不可变的区别在于对象本身存放的数据是否可变
分类在python的内置类型中:
- 可变对象:list,dict,set
- 不可变对象:tuple,string,int,float,bool
- 在对可变对象的内容进行修改后,地址是没有发生改变的
- 而对不可变对象而言,改变对象的值后,会重新为其生成一个新的地址存放改变的数据
- 可变对象作为函数参数传递时,会在函数中进行修改,可以影响到全局的变量值
- 不可变对象,改变对象的值后,会生成一个内存地址来存储改变的值
- 深拷贝:将对象的所有内容拷贝一份,放于新的地址中
- 浅拷贝:将引用拷贝一份,地址不变
- 赋值时将一个对象的地址赋给一个变量,让变量指向该地址
- 修改不可变对象时,需要开辟新的内存空间,深拷贝一份对象
*args和**kwargs只是为了方便并没有强制使用它们.
- *args 是当你不确定你要传递多少参数时使用。其可以传递任意数量的参数。
- **kwargs允许你使用事先没有先定义的参数名
- *args必须在**kwargs前面使用
简言之、这种在运行时,编译时,类和方法加载时,动态地将代码切入到类的指定方法、指定位置上的编程思想就是面向切面的编程。
我们管切入到指定类指定方法的代码片段称为切面,而切入到哪些类、哪些方法则叫切入点。有了AOP,我们就可以把几个类共有的代码,抽取到一个切片中,等到需要时再切入对象中去,从而改变其原有的行为。
优点是:这样的做法,对原有代码毫无入侵性
22,新式类和旧式类旧式类:没有继承object的类; 在早期的Python版本中,程序员在定义类的时候必须要指明类继承了object,类不会默认继承object,这些没有继承object的类就称为旧式类。 新式类:继承object的类 ; 与上面相反,继承了object类的子类就叫做新式类,而在python3中,所有的类都会默认继承object类,所以python3中的类都是新式类。23,read,readline和readlines
- read 读取整个文件
- readline 读取下一行,使用生成器方法
- readlines 读取整个文件到一个迭代器以供我们遍历
函数式编程是一种编程范式
命令式编程命令式编程 是面向计算机硬件的抽象,有变量、赋值语句、表达式、控制语句等,可以理解为 命令式编程就是冯诺伊曼的指令序列。 它的主要思想是关注计算机执行的步骤,即一步一步告诉计算机先做什么再做什么。声明式编程
声明式编程 是以数据结构的形式来表达程序执行的逻辑。它的主要思想是告诉计算机应该做什么,但不指定具体要怎么做。SQL 语句就是最明显的一种声明式编程的例子函数式编程
- 而函数式编程和声明式编程是有所关联的,因为他们思想是一致的:即只关注做什么而不是怎么做。但函数式编程不仅仅局限于声明式编程。
- 函数式编程是面向数学的抽象,将计算描述为一种表达式求值,其实,函数式程序就是一个表达式。
闭包使得局部变量在函数外访问成为可能
为什么要使用闭包闭包避免了使用全局变量,此外,闭包允许将函数与其所操作的某些数据(环境)关连起来。这一点与面向对象编程是非常类似的,在面对象编程中,对象允许我们将某些数据(对象的属性)与一个或者多个方法相关联。总结
闭包是一种函数,它保留了定义函数时存在的自由变量的绑定,因此,即便是函数返回后作用域不存在了,那些绑定仍然能够被使用。闭包可以很容易地实现一些简单的类的功能,同时基于此还有很多Python的魔法能够实现,比如装饰器。



