Python高级编程相关知识

本文深入探讨了Python编程的高级主题,包括一切皆对象的概念,如魔法方法、抽象基类、类变量和实例变量。讨论了鸭子模型和元类,介绍了序列、dict和set的实现原理,以及如何处理多继承问题。同时,文章涵盖了Python中的并发编程,如线程、进程、协程和事件循环,详细讲解了Socket实现的聊天和HTTP请求。此外,还讨论了GIL全局解释器锁对多线程的影响,以及如何利用futures和asyncio进行异步编程。
摘要由CSDN通过智能技术生成

一切皆对象

所有对象都是type对象的实例,type本身也是,还有就是type和object之间是鸡与蛋的关系

print(type(list))  # >>> type
print(type(type))  # >>> type
print(type(object)) # >>> type

print(type.__bases__)  # >> object

常见的内置类型

内置类型的三大特征:id,type,value

  1. None
  2. 数值类型:整形,浮点型
  3. 迭代类型
  4. 序列类型
  5. 映射类型dict
  6. 集合类型set frozenset
  7. 上下文管理类型 可使用with
  8. 其他类型:函数 等...

魔法方法

这类方法一般是通过双下划线开头和结尾:例如熟知的__init__ __new__ __getitem__ __setitem__等众多方法,通过实现魔法方法,可以让python对象更加灵活的使用。

  • 特殊的
    python语言原生的数据结构,例如list,set,dict,它内部是通过c语言实现,所以在len([1,2,3])时会走捷径,所以效率会快一点。

例如: 如果实现了__enter__方法和__exit__方法,或者使用contextlib简化上下文管理类型,得到的对象可以使用上下文管理器with

import contextlib
@contextlib.contextmanager
def fileopen(filename):
	print('enter')  # 打开逻辑
	yield {
   }   # yield应该返回打开的文件的句柄
	print('exit') # 退出逻辑

with - 嵌套会让代码的可读性不好,所以可以使用这个嵌套的语法

# 嵌套:
with open('1.txt','wb') as f1, open('2.txt','rb') as f2:
	# do with f1 and f2

鸭子模型

会呱呱的就是鸭子,会giao人人都是giao哥

例如:list.extend()这个方法接收一个对象,只要这个对象实现了可迭代协议,就可以使用这个方法添加到列表中,这就是鸭子模型接受的参数不一定是某个对象的子类,但是一定实现了对象的某些方法

from collections.abc import Iterable
alist = []
if isinstance(a,Iterable):
	alist.extend(a)  # >> success
# 注意list的实现是c语言,所以在他和python肯定是有一些对接的问题,list不是Iterable的子类,但list对象是。

list不是Iterable的子类,但list对象 是

抽象基类(abc)

  • 场景: 想让子类必须实现一些方法,否则抛出异常
  • 不宜过多使用,防止定制过度

type 和isinstance区别

  • type判断一个对象是谁的实例
  • isinstance是判断这个对象的继承链,isinstance(a,b)如果a在b的下方就返回True,否则False

类变量和实例变量

  • 定义在类中的变量是类变量,在初始化实例的时候定义的变量是实例变量
  • 从继承的角度来讲:实例变量可以继承类变量,如果为实例变量赋值,那么在查找时就直接找实例变量,此时删除实例变量,又会去到类变量寻值。
  • 类变量是全局可变的,动态的修改了类变量,那么实例中找类变量时是改变后的值
    如果把实例看成是类,那么类变量就是父类中的属性,而实例变量是子类的属性,子类中没有父类有 会从父类中继承,子类中有父类有 相当于覆盖重写
class  A:
    name = 'bb'
    def __init__(self,x):
        self.x = x

a1 = A(1)
a2 = A(2)
print(a1.name) # 继承类变量bb
print(a1.x)  # 就是x
print(A.name) # 类变量bb

A.name = 'dd'  # 更改了类变量
print(A.name) # 类变量发生更改dd
print(a1.name) # 实例的类变量也更改dd

a1.name = 'namne' # 为实例属性赋值
print(a1.name) # 不去找类变量,直接就是自己的属性name
print(A.name) # 类变量属于父类,所以还是dd

A.name = 'last'  # 在此更改类变量
del a1.name # 删除了实例属性
print(a1.name) # 继承类变量last	

类变量和属性的调用顺序

在python中,为了重用代码,可以使用在子类中使用父类的方法,但是在多继承中可能会出现重复调用的问题,例如菱形继承关系

class A:
	def __init__(self,*args,**kwargs):
		self.sex = sex
class B(A):
	def __init__(self,*args,**kwargs):
		A.__init__(self,*args,**kwargs)
		self.name = name

class C(A):
	def __init__(self,*args,**kwargs):
	   A.__init__(self,*args,**kwargs)
	   self.age = age
class D(B,C):
	def __init__(self,*args,**kwargs):
		B.__init__(self,*args,**kwargs)
		C.__init__(self,*args,**kwargs)
	
print(D.mro())

# 实际上会去先找B,然后找A,然后是C,然后是A

为了防止多继承中的一些问题,使用C3算法解决,它能够保证调用链中所有的类仅出现一次,也就是说每个节点后面的类都不继承它,否则将它从调用链中删除,D.mro()查看调用链。

super()的使用就是基于这条调用链,当使用super()时,会查找调用链,找到当前类,调用写一个类,没有则找下一个。super()默认是当前类,也可以写类名

静态方法、类方法、实例方法

  1. 静态方法:@staticmethod
    和类的关系不是很大,但是又显得不可缺少,较普通函数来说,和类关系密切
  2. 类方法:@classmethod
    和类的关系密切,接收必须参数cls表示当前类,因为有了cls,所以创建类的时候方便,修改类变量或者修改属性也比较方便
  3. 实例方法:
    它是实例的方法,接收必须参数selfself表示当前实例,因为有self实例,所以在修改实例变量或者属性的时候会很方便

Python通过双下划线来实现私有属性 : 但并不是不可查看,只是一种规范

class  A:
    def __init__(self,x):
        self.__x = x
a = A(1)

print(a._A__x)  # >>> 1  私有属性也查看到了

私有属性的getter和setter: 通过property来实现

def Rec:
	def __init__(self,x,y):
		self.x = x
		self.y = y
		self.__z = x*y  
	
	@property		 # 为了让z可以向属性一样调用使用property
	def z(self):
		return self.__z
	@z.setter   # 可以使用setter来实现订阅-发布的功能,当新的setter时执行时,for循环遍历订阅者。发布订阅
	def z(self,z):  
		if isinstance(z,type): # 判断
			self.__z = z
		else :
			raise "z必须是怎么怎么样的"

多继承问题

因为多继承会有条调用链,而这条调用链是某种算法计算得到的,一般认知可能会出错,所以在使用的时候,可以先查看调用链在使用类.mro()

或者使用mixin的模式:在DRF中使用了mixin模式多继承:特点就是 :
1.每个mixin类的特点都比较单一,各个mixin类之间基本没有相同代码,所以不要使用super去继承代码
2. 不和基类关联,可以任意基类组合每个mixin都是组件,可以任意选配

序列

序列类型:

  1. 容器类型:list,tuple,deque,Queue内部实现使用的是deque
  2. 扁平序列:str,bytes,bytearray,array.array
  3. 可变序列:list,deque,bytearray,array
  4. 不可变序列:str,tuple,bytes

容器抽象基类都放在collections.abc里面

from . import (
    Container as Container,
    Hashable as Hashable,
    Iterable as Iterable,
    Iterator as Iterator,
    Sized as Sized,
    Callable as Callable,
    Mapping as Mapping,
    MutableMapping as MutableMapping,
    Sequence as Sequence,
    MutableSequence as MutableSequence,
    Set as Set,
    MutableSet as MutableSet,
    MappingView as MappingView,
    ItemsView as ItemsView,
    KeysView as KeysView,
    ValuesView as ValuesView,
)

例如:是Container类实现了__contains__魔法方法,没实现解释器会去看看是否有__getitem__等方法曲线救国,这是解释器的隐式优化

一个对象通过继承多个抽象基类得到新的对象,有点像drf的mixin。

class Sequence(Reversible, Collection):

class Collection(Sized, Iterable, Container):
# Sized --> __len__
# Iterable --> __iter__  __next__
# Container --> __contains__  __getitem__
class Reversible(Iterable):

切片 ☆☆☆☆ [ : : ]

可以通过接片实现插入查看插入和替换,而替换为None则实现了删除:操作如下

alist = [1,2,3,4]
# 切片时第一个冒号前面默认为0,第二个冒号前默认为-1,最后一个默认为1,第二个位置值过大的不合法就会变为合法值

### 查询  创建
a = alist[::]  # 得到一个新列表
b = alist[::-1]  # 得到逆序列表

c = alist[::2]  # 隔一个取一个,从0开始,也就是偶数位
d = alsit[1::2]  #  隔一个去一个 ,从1开始,也就是基数位

### 插入

alist[len(alist):] = [9]  # 在尾部添加元素9 >>[1,2,3,4,9]
alist[-1:-1]  # 在倒数第二个元素位置添加元素9 >> [1,2,3,9,4]  因为-1取不到,所以是倒数第二个值
alist[0:0] = [0,0] # 在头部添加元素9,9 >> [9,9,1,2,3,4]
alist[2:2] = [1,2]  # 在指定位置添加1和2 

### 替换
a[::] = [1,2,3]  # 从0到最后全部替换

a[::2] = [1,2] # 隔一个替换一个  两边长度相等

需要特别注意

  1. 切片对象返回的还是列表,给切片对象赋值时插入和替换也要使用列表
  2. 当等号左边切片不连续,那么在替换的时候右边的长度要保证和左边相等,连续就是全替换
  3. 左闭右开区间

列表的extend接收到是可迭代对象,而+=必须保证符号两边是相同类型,+=是魔法函数实现的,extend是单独实现的

实现可切片对象★★★

Sequence

class Sequence(Reversible, Collection):
# 继承自Reversible,所以需要实现__reversed__
# Collection又继承Sized,Iterable,Container 需要实现__len__,__iter__,__contains__

如果要实现类似的可切片对象其实需要实现相应的魔法方法__getitem__即可

import numbers


class Group:

    def __init__(self, staffs):
        self.staffs = staffs

    def __reversed__(self):
        pass

    def __getitem__(self, item):  # 切片的关键
        # return self.staffs[item]   #让list代替,但是返回是list类型,所以要生成一个改类的类型
        cls = type(self)
        if isinstance(item,slice):
            return cls(staffs = self.staffs[item])  # 如果是slice对象
        elif isinstance(item,numbers.Integral):
            return cls(staffs = [self.staffs[item]]) # 如果是int类型,返回某个元素,但是创建类接收的是列表,所以要【】

    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 = ['user', 'admin1', 'admin2']

group = Group(staffs=staffs)
sub = group[::2]

if 'user' in sub:
    print('Y')
bisect 模块 ·序列·

使得他们可以在插入新数据仍然保持有序,避免先创建在排序而浪费资源

只要是序列就可以使用

bisect = bisect_right
insort = insort_right
# 默认都是后面
import bisect

alist = []
bisect.insort(alist,3)
bisect.insort(alist,2)
bisect.insort(alist,4)
bisect.insort(alist,6)
bisect.insort(alist,5)
print(alist)

print(bisect.bisect(alist,3.1))

# >>>[2, 3, 4, 5, 6]
# >>>2
什么时候duck不必使用list

序列中数据类型一致时,使用array会更好,因为array是一块连续的存储空间,list是不连续的

  • 区别: list可以装载不同类型的对象,而array只能装相同数据类型,且要先声明
import array
a = array.array('i')  # i表示int d表示double f表示float u表示unicode 等
a.append(1)
a.append(1)
a.append(1)
print(a)

dict和set

get(key,default): 若key不存在则返回default
setdefault(key,default):若key不存在则返回default,并且将default设置到字典中

a_dict = {
   'q':'Q','w':'W'}
print(a_dict.get('r','R'))
print(a_dict.setdefault('q','QWER'))
print(a_dict)
a_dict.setdefault('r','R')
print(a_dict)

'''output
R
Q
{'q': 'Q', 'w': 'W'}
{'q': 'Q', 'w': 'W', 'r': 'R'}
'''

update方法:合并字典: 接受的参数类型可以是字典,可以是关键字参数的格式,也可以是元组的元组

除了update方法,还可以用强制类型转换的方法合并字典new_dict = dict(a_dict,**c_dict)

a_dict = {
   'q':'Q','w':'W'}
b_dict = {
   'r':'R'}

#
a_dict.update(b_dict)
print(a_dict)

#
a_dict.update(i='I')
print(a_dict)

#
a_dict.update((('z','Z'),(
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值