# PythonLearning **Repository Path**: wll1990310/PythonLearning ## Basic Information - **Project Name**: PythonLearning - **Description**: No description available - **Primary Language**: Python - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2018-12-27 - **Last Updated**: 2020-12-19 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Python基础知识总结 ## 数据结构 - 基本数据类型 - 整数,二进制,十进制,十六进制(0x开头) /精确出发, //地板除 - 浮点数,运算可能四舍五入 - 布尔值,True | False (and,or,not) - 空值,None - 字符串,'' | "" 。\表示转移,r''表示字符串内容默认不转义,b''表示字符串内容的数据是byte类型 - python3默认使用Unicode编码。 - ord():字符的整数表示, chr():整数转换为字符 - encode() 将字符串编码为指定类型,默认utf-8, decode()将字节解码为字符串。 - len('') 计算字符串或字节长度 - 格式化: '%[s,d,f,x]'占位符; format()替换字符串中的{index} - 集合 - list 列表,有序的集合 - 初始化: list = [] - 增: append(element) , insert(index, element) - 删: pop(), pop(index) - 查: 循环遍历, list[index] - tuple 元组,有序但不可修改 - 初始化: list = (,) - dict 字典 - 初始化:dict = ['key': value] - 查: dict['key'] 如果元素不存在会报错 , dict.get('key', 1) 如果元素不存在不会报错 - set 集合,没有重复的element - 初始化:s = set([]) ,即set的创建依赖list作为输入集合 - 增: add(element) - 删: remove(element) - 变量与常量 python变量是弱类型即生成时确定类型 都是可更改内容类型,常用以大写字符表示,但仍可更改。 ## 逻辑流程 - 条件 if condition: pass elif condition: pass else: pass - 循环 - for ... in 依次将tuple|list中的元素遍历 - range(count) 生成整数数列 - while condition: pass - break:结束当前循环,continue:跳过本次循环 ## 函数 - 参数 - 位置参数 `def position_param(param): print("param=%s" % param)` - 默认参数 通常默认参数位置靠后,且必须指向不变对象 `def default_param(param, p='default'): print('param=%s, p=%s' % (param, p))` - 可变参数 参数前加*,函数内部接收到的参数是个tuple。如果要将list传入,可以在list参数前加* `def flex_param(param, *args): print('param=%s' % param, ', args type is {0}'.format(type(args)), args) flex_param('first', ['a', 'b']) list_param = ['a', 'b'] flex_param('first', *list_param) flex_param('first', ('a', 'b',))` - 关键字参数 参数前加**,函数内部接收到的参数是个dict。调用函数拆参数时方式类似。 `def key_param(param, **kw): print('param=%s' % param, 'kw type is {0}'.format(type(kw))) for k in kw: print('k={0}, w={1}'.format(k, kw.get(k))) dict_param = {'one': 1, 'two': 2} key_param('不能直接将dict传入,需要通过**拆包后传入', **dict_param) key_param('key=value方式传入', one='wang', two='tu')` - 命名关键字参数 - 与关键字参数区别在于*后的参数的key指定为函数签名中定义的字面值,不可随意传递 - 关键字参数前一参数用*指定。如果*位置的参数是由可变参数指定,则不再需要另外添加*。 `def key_name1_param(param, *, param1, param2): print('param1={0}, param2={1}'.format(param1, param2)) def key_name2_param(param, *args, param1, param2): print('args={0}, param1={1}, param2={2}'.format(args, param1, param2)) key_name1_param('first', param1='1', param2='2') ps = ['a', 'b'] key_name2_param('second', *ps, param1='1', param2='2')` - 参数组合 有顺序区分,依次为:必选参数、默认参数、可变参数、命名关键字参数、关键字参数 `def amazing_param(*args, **kw): print('可以表示所有的参数类型, args={0}, kw={1}'.format(args, kw)) ap = ['a', 'b', 'c'] bp = {'a': 1, 'b': 2} amazing_param(ap, 'over', **bp)` - 返回值 函数默认都有返回值。如果通过return定义则返回指定类型。否则默认返回None ## 高级特性 - 切片 类似Java中的subString。 通过[start : end]方式切割, 前开后闭区间。 `l = [1, 2, 3, 4] l1 = l[0: 3] # [1, 2, 3] l2 = l[1: 3] # [2, 3] l3 = l[:] # [1, 2, 3, 4] l4 = l[:3] # [1, 2, 3]` - 迭代 - 无论是否有下标,如果容器是可迭代的,则具备迭代的能力。 'dict1 = {'a': 1, 'b': 2, 'c': 3} for i in l: print('index=%s' % i) for k in dict1: print('k=%s , value=%s' % (k, dict1.get(k))) for k, v in dict1.items(): print('k=%s , value=%s' % (k, v))` - 判断集合是否是可迭代的 Iterable `from collections import Iterable print('判断是否是可迭代的', (isinstance('abc', Iterable))) # True` - 遍历集合过程中获取下标 enumerate `# index= 0, value= 1 index= 1, value= 2 index= 2, value= 3 index= 3, value= 4 for index, value in enumerate(l): print('index= %s, value= %s' % (index, value), end=" ")` - 列表生成器 - 简化列表生成复杂度 `# 生成全排列 ['AD', 'AE', 'AF', 'BD', 'BE', 'BF', 'CD', 'CE', 'CF'] l = [m + n for m in 'ABC' for n in 'DEF'] print('生成全排列', l)` `# 当前目录下文件: ['.idea', 'Advantages.py', 'Functions.py', 'README.md', 'venv'] dirs = [d for d in os.listdir('.')] print('当前目录下文件:', dirs)` - 生成器 - 解决列表生成器运行时产生的数据量过大占用内存问题,在获取元素时通过算法生成数据 - [] -> ()方式确定生成器。 `l2 = (x for x in range(10)) print(next(l2), next(l2), next(l2)) for item in l2: print('item=%s' % item, end=',')` - 通过yield关键字, 使用了yield的函数就是生成器而非普通函数。yield是函数在调用过程中会block,直到有终止语句为止。 `def fib(m): n, a, b = 0, 0, 1 while n < m: yield b b, a = a+b, b n = n+1 f= fib(5) for n in f: print('n=%s' % n, end=',') g = fib(6) while True: try: print('next=%s' % next(g)) except StopIteration as e: print('catch Exception') break` - 迭代器 Iterator 区别于 Iterable,Iterator是一种惰性运算的对象。生成器返回的对象可以认为是迭代器。 可迭代未必是迭代器。能被next()函数调用返回一定是可迭代的。可以通过iter()函数将可迭代对象转化为迭代器。 ## 函数式编程 - Python并非纯函数式编程,但是仍旧能做到函数作为输入与输出函数。Python中的函数也是变量。 - map/reduce/filter/sort - map map函数接收两个参数:函数变量 与 可迭代对象。 返回迭代器 - reduce 接收两个参数:函数变量 与 可迭代对象。 函数变量接收两个参数,分别是已经处理了的迭代对象的元素和待处理的下一个元素 `def proc(): float_str = '123.456' key_map = {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9, '.': -1} float_arr = map(lambda x: key_map[x], float_str) point = 0 def str2float(f, n): nonlocal point print('f,n={0},{1} point={2}'.format(f, n, point)) if n == -1: point = 1 return f if point == 0: return f * 10 + n else: point = point * 10 return f + n / point result = reduce(str2float, float_arr) print(result)` - filter 过滤器,接收函数+可迭代对象,返回迭代器。 `l = [1, 2, 3, 4, 5, -1, -2, -3, -4] result = filter(lambda x: x > 0, l) for item in result: print(item, end=' ')` - sorted 按照key对迭代器进行排序,关键是选用的key `l = ['ab', 'cc', '2e', 'B', 'P'] def sort_function(n): if len(n) >= 2: try: return int(n) except Exception as e: return 0 else: return -1 result = sorted(l, key=str) result2 = sorted(l, key=sort_function)` - 返回函数 关键概念是【闭包】,即将参数和结果都保存在返回的函数中。 注意【闭包】非立刻执行,因此不得引用外部循环变量或者会发生变化的变量,如果必须使用,则定义函数包裹,在使用到目标函数时, 测试的变量已经被保存到目标函数中。 ` def f(): def h(i): def g(): return i * i return g fs = [] for i in range(1, 4): fs.append(h(i)) return fs` - 匿名函数 即lambda表达式,:前为参数,要求:后面的表达式直接返回值,没有return语句 - 偏函数 简化默认函数的调用方式 ` print(int('010101010',base=2)) from functools import partial int2 = partial(int , base=2) print(int2('010101010'))` - 装饰器 可以通过继承和组合的方式实现从语言的层面实现装饰器。只要在被装饰的函数签名前添加@xxx即可。在被装饰的函数调用时, 实际调用的对象是装饰器中返回的wrapper函数。 为处理装饰器上添加参数的问题,可以通过两种中方式处理: 1. 通过嵌套定义装饰器的方式,在内层的函数中定义参数。但是这种方式的问题在于 被装饰函数的签名被篡改。 `def advance_log(text): # 再多定义一层函数,以方便advance_log定义参数 def decorator(func): def wrapper(*args, **kw): print('****' * 10, text, func.__name__) return func(*args, **kw) return wrapper return decorator # 在此添加标识此函数被装饰 @advance_log('adv') def log(msg): print(msg)` 2. 通过Python内置的functool的wrap标识来方式被装饰函数的签名被篡改,并实现装饰器参数的传递。 ` def advance_log2(func): @functools.wraps(func) def wrapper2(*args, **kw): print('****' * 10, func.__name__) return func(*args, **kw) return wrapper2 @advance_log2 def log2(msg): print(msg)` ## 模块管理 - 包含__init__.py的目录为单独的包,每一个python文件即单独的模块。不同模块下的函数变量可重名,不同包下的模块可重名。 - 通过import 方式引用包, 可以通过as 为模块设置别名以处理不同模块下的重名问题。 - __value__ 标识私有成员, __function标识私有函数。未从语言级别限制,只是一种编程规范。 - 通过pip 方式安装包或者通过anaconda引入包管理器。 - 可以通过修改sys.path的方式在运行期间添加模块搜索路径。 ## 面向对象 - 类 变量和函数的封装模板。__init__函数指定实例化对象时的参数。区别于静态语言,实例化的对象可以在对象运行期间动态的绑定属性。 `class Person(): def __init__(self, age, name): self.age = age self.name = name p = Person(23, 'ali') p.sex = 'male' q = Person(32, 'baba') print(p.name, p.age, p.sex) print(q.name, q.age, q.sex) # AttributeError: 'Person' object has no attribute 'sex'` - 访问修饰符 - private Python没有关键字声明变量是private类型,只能通过在变量名前加__标识。还有一种类型是__{name}__变量,这种变量属于特殊变量, 一般不建议在外部访问。还有一种是_{name}变量,这种方式用于声明该变量是私有变量,不建议外部访问。 - Python实现私有变量机制的原理: 在__{name}__的变量前加了_{className}的前缀,即外部通过_{className}__value的方式依旧 可以访问到私有变量,但是不建议这么使用。 `class Man(): def __init__(self, age, name, weight): self.__age = age self.__name = name self.__weight__ = weight man = Man(32, 'ali', 190) print(man.__weight__, man._Man__age) # 这里强制访问私有变量方式` - 继承 - 通过在声明的括号中写明父类即可。 - 通过isinstance(object, father)判断对象是否是子类对象。 - 不同于静态语言类型,Python遵循‘鸭子’原则,即有需要的函数即可实现多态。 `class Parent(): def live(self): print('live') def breath(self): print('breath') class Son(Parent): def live(self): print('live son') # 继承父类的成员函数以及变量 son = Son() son.live() son.breath() # 通过isinstance判断是否是类实例 print(isinstance(son, Parent)) # 鸭子 模型 def breath(anim): anim.breath() breath(son)` - 对象信息 - type 获取实例类型 - isinstanceof 判断对象类型匹配 - dir 获取类的所有属性和函数 - has/set/getatter 用于未知对象信息时去设置对象的属性,类似JAVA的反射 `class Person(): def __init__(self, sex): self.sex = sex p = Person('male') ds = p.__dir__() print(ds) # ['sex', '__module__', '__init__', '__dict__', '__weakref__', '__doc__'... print(hasattr(p, 'sex')) setattr(p, 'sex', 'female') print(getattr(p, 'sex', 'unknow'))` - 实例属性与类属性 - 实例属性 通过self绑定; 类属性通过在类中声明定义,且该属性属于类,在实例对象中修改该属性仅仅作用于该实例,类中 该实例的值并不会改变 - 实例属性与类属性定义重名会导致实例的属性名称覆盖类的属性名称的问题 `class Person(): name = 'Person' def __init__(self, age): self.age = age p = Person(32) print(p.name) p.name = 'jack' print(p.name) print(Person.name)` ## 面向对象高级编程 - __slots__ 值为tuple, 运行时可以给对象动态绑定函数和变量,如果声明__slots__值,则只有__slots__中声明的变量或者函数可绑定 绑定不影响其他的实例。也可以在类中绑定,所有实例就具备新变量,但子类不会继承__slots__。 ` class Person(): def __init__(self, age=12): self.age = age __slots__ = ('age', 'name', 'pick', 'say') def __str__(self): return 'name={0}, age={1}'.format(self.name, self.age) p = Person() p. name = 'bb' def say(self): pass from types import MethodType p.say = MethodType(say, p) p.xx = 'xx' # AttributeError: 'Person' object has no attribute 'xx'` - @property 为简化getter,setter函数调用方式存在。s/getter函数直接以变量命名,定义@property注解后,会自动生成@xxx.setter注解 `class Student(): def __init__(self, age): self._age = age @property def age(self): return self._age @age.setter def age(self, age): self._age = age s = Student(32) print(s.age) s.age = 31121 print(s.age)` - 多重继承, MixIn模式 即子类可以有多个父类,继承各个父类的成员。如果多个父类中有重名问题,则按照顺序优先处理。 ` class Male(): def sex(self): print('thick') class Female(): def breat(self): print('soft') def sex(self): print('female sex') class LALA(Female, Male): pass lala = LALA() lala.sex() lala.breat()` - 枚举类 通过内置的Enum实现枚举,默认枚举类的值为value且从1开始。获取枚举类的值可以直接或者通过value的方式获取。 `from enum import Enum,unique GENDER = Enum('gender', ('male', 'female')) print(GENDER['female']) print(GENDER.male) print(GENDER(1)) # 通过value获取时,默认value从1开始,如果为0会报错 # 自定义枚举类 @unique class SEX(Enum): male = 0 female = 1 print(SEX['female']) print(SEX.male) print(SEX(0))` - 定制类(操作符或函数重写) - __str__ - __iter__ + __next__ 实现for循环效果 ` class My_list(): def __init__(self, list): self.index = 0 self.a = list def __iter__(self): return self def __next__(self): i = self.index + 1 if i == len(self.a): raise StopIteration() else: self.index = self.index + 1 return self.a[i] ml = My_list(range(10)) for i in ml: print('i = {0}'.format(i))` - __getitem__ 实现通过索引获取值 class My_list(): def __init__(self, list): self.index = -1 self.a = list def __iter__(self): return self def __next__(self): i = self.index + 1 if i == len(self.a): raise StopIteration() else: self.index = self.index + 1 return self.a[i] def __getitem__(self, item): if isinstance(item, int): if item <= len(self.a): return self.a[item] else: return -1 elif isinstance(item, slice): return self.a[item.start: item.stop] ml = My_list([5, 6, 7, 8]) for i in ml: print('i = {0}'.format(i)) print(ml[3]) print(ml[0:3]) ## IO - 文件读写 文件读写依赖系统接口,分为同步和异步。可以读写文本或二进制文件,读写后必须关闭资源。同样可以有创建删除文件、文件目录 等功能。 - 常用接口 - 读/写流程 - open -> read/write -> close - 文件读写必须关闭,可以通过try except finally 或者with实现系统管理资源的释放 - 文件处理的模式 - 只读 r 可以指定读取内容的大小,read(size), readline(), readlines()返回list。 - 写 w 默认会将源文件覆盖,通过'a'方式写入会在源文件后添加内容,可以控制输出行数。 - 文本/二进制 默认以文本文件方式读取,如果读取二进制文件会报:UnicodeDecodeError: 'utf-8' codec can't decode byte 0xff in position 0: invalid start byte的错误 二进制文件读写应该以rb\wb方式打开文件。 ` def read_file(file_path): # with open(file_path, mode='r', encoding='UTF-8') as f: 读文本文件 # with open(file_path, mode='w', encoding='UTF-8') as f: 写文件,用'w'模式会将源文件覆盖 # with open(file_path, mode='a', encoding='UTF-8') as f: # 写文件,在源文件后添加内容,不覆盖 with open(file_path, mode='rb') as f: # 以二进制方式打开文件 content = f.read() print(content) np = os.path.join(os.path.abspath('.'), 'zly.jpg') read_file(np) - StringIO/BytesIO - 接口位于io包下 - 类似Java BytesArrayInputStream/BytesArrayOutputStream,所谓写入写出指的是相对内存而言。StringIO和BytesIO可以控制将 内容从字符串/字节数组中读入,或写出。` - 通过write将内容写入到内存,通过getValue读取写入的内容;通过构造函数将内容写入内存, 通过read方式从内存读出。 `from io import BytesIO, StringIO def io_utils(): strIO = StringIO() strIO.write('hello, stil') print(strIO.getvalue()) lines = StringIO('abcdefghijklmn\r\nopqrstuvwxyz').readlines() for line in lines: print(line) def byte_utils(): br = BytesIO('hello, stil'.encode(encoding='UTF-8')).read() print(br.decode(encoding='UTF-8')) bio = BytesIO() bio.write('abcdefghijklmnopqrstuvwxyz'.encode(encoding='UTF-8')) bv = bio.getvalue() print(bv) print(bv.decode(encoding='UTF-8'))` - 序列化 - 字节码序列化 - 包:pickle;可以以内存或者文件为序列化的目标。 - 读写:load(s) / dump(s) `import pickle class Person(): def __init__(self, name, age): self.name = name self.age = age def __str__(self): return 'name={0}, age={1}'.format(self.name, self.age) pbytes = pickle.dumps(Person('magoo', 23)) print(pbytes) pl = pickle.loads(pbytes, encoding='UTF-8', errors='ignore') print(pl) np = os.path.join(os.path.abspath('.'), 'README1.md') with open(np, 'wb') as f: pickle.dump(Person('magoo', 23), f) with open(np, 'rb') as f: person = pickle.load(f) print(person, 'load>>>>>>>>>>>>>>>>>>>>>>>>>>>>')` - JSON序列化 - 对于自定义类,需要在序列化过程中声明:default / object_hook指明序列化类型,default的值可以以obj.__dict__代替,注意 定义了__slots__的类不能这么做,需要声明对象转dict函数。object_hook的值为dict转对象的映射函数 - 读写:load(s) / dump(s) `import json pstr = json.dumps(Person('magoo', 23), default=lambda obj: obj.__dict__) print(pstr) def dict2json(d): return Person(d['name'],d['age']) person = json.loads(pstr, object_hook=dict2json) print('>>>', person, 'type={0}'.format(type(person)))` - 文件及目录操作 - 文件路径 os.path.abspath('.') 获取当前目录 os.path.join(path, dest) 将dest路径拼接在path路径后 - 创建,删除文件目录/文件 os.mkdir(filepath) # 创建目录,如果以存在会报错 os.rmdir(filepath) # 删除目录 os.rename(filepath, newname) # 重命名文件 os.remove(filepath) # 删除文件 文件的拷贝等操作可引入shutil包处理 - 文件名操作 os.path.split(filpath) # 获取叶子节点名称 os.path.splitext(filepath) # 获取文件名后缀,如果为目录,则没有后缀 # 进程与线程 - 进程运行于独立的内存空间,单核CPU无法真正执行多任务,只有多核CPU才能真正意义上执行多任务。线程是在进程内部执行的任务, 多线程共享进程中的内存空间。在Python中,由于GIL锁,导致处理并发任务时,使用进程比较多,线程处理并发任务在Python中难以实现。 - 进程 - 为跨平台使用,建议使用multiprocessing下的接口 - 启动进程方式: Process(target=function)构建进程,然后start即可,通过Process的join函数,可以要求当前进程完成后再继续处理任务。 - 多进程: 通过Pool池对象管理较为方便,通过apply_async方式添加进程任务。在close函数被调用后,Pool中不能再添加任务。 - 进程间通信:使用Queue方式通信,但注意如果使用Queue通信,不能以Queue实例作为参数传入Pool构建的进程池创建的进程中,Queue只能作为 参数传入Process的子类中。猜测通过Pool实现的进程池并非真正的进程,而是伪进程。如果使用apply方式传递会报错: `Queue objects should only be shared between processes through inheritance` - 进程的输入输出: 通过subprocess包接口处理 - 线程 - 通过threading创建线程Thread,参数传递方式以及制定线程运行代码与进程方式相同 - 线程安全-锁 线程共享全局变量会有线程安全问题,与java类似,通过添加lock锁可以控制代码块的原子方式执行。 - ThreadLocal - 线程本地变量,即单独定义一份thread.local()以后, 可以在各个线程使用该变量之前对threadlocal内进行赋值,这样各个线程中 获取该变量时就比较方便,且安全(即各个线程中对该变量的改变不会影响到其他线程) - 示例代码:ProcessAndThread.py - 通过 BaseManager实现分布式 代码:DistributionMaster.py , DistributionWorker.py ## 网络编程 - 凡是涉及各种语言的网络编程模块,都逃不开网络模型的讲解,虽然语言不同,但是基本流程相同。 - TCP/UDP - 依赖库 socket - TCP - 创建socket 对象 socket.socket(socket.AF_INET, socket.SOCK_STREAM) 参数1表示IPV4, 参数2表示TCP传输数据格式 - 监听端口 sock.bind(tuple(IP地址, 端口号)) - 接收数据 sock.listen() 监听的等待连接的数量。 sock, addr = sock.accept() sock是socket连接对象, addr 是客户端地址, sock.recv() 获取请求的byte类型数据 - 发送数据 sock.send(字节数据) - 关闭连接 sock.close() 代码示例:net/Server_TCP, Client_TCP - UDP - 创建socket 对象 socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 参数1表示IPV4, 参数2表示UDP传输数据格式 - 接收数据 data, addr = socket.recvfrom(缓冲区大小) data表示接收到的数据, addr表示数据源(ip, port) - 发送数据 sock.sendto(字节数据, addr) addr是目标的地址(ip, port) - 关闭连接 sock.close() 代码示例:net/Server_UDP, Client_UDP ## 数据库 - Python自带数据库是sqlite - 数据库使用的步骤类似:创建连接 -> 获取游标 -> crud -> 提交修改 -> 关闭游标 -> 关闭数据库连接 - Sqlite - connection = sqlite3.connect({db名称}) 此时创建数据库 - c = connection.cursor() 获取游标 - crud 均通过cursor.execute执行sql语句, 通过cursor.fetchall()可以获取到所有及记录,返回结果为tuple列表,通过cursor.rowcount()获取影响到的所有行数 - connection.commit() 提交修改 - c.close() 关闭游标 - connection.close() 关闭数据库 - Mysql - 首先安装Mysql,启动Mysql服务进程 - 安装python连接MySql的库 - 其余crud操作与sqlite相同 - SQLALchemy - ORM映射工具。需要安装Python三方库。 - 参考代码:SQLALchemyDB.py