python3学习笔记:零碎知识点

  • break语句可以在循环过程中直接退出循环,而continue语句可以提前结束本轮循环,并直接开始下一轮循环。这两个语句通常都必须配合if语句使用。
  • list:Python内置的一种数据类型叫列表。list是一种有序的集合,可以随时添加和删除其中的元素。格式:[ ]
    • 要删除指定位置的元素,用pop(i)方法,其中i是索引位置:
    • 可以获取倒数第2个、倒数第3个L[-2]、L[-3]
  • tuple:另一种有序列表叫元组。tuple和list非常类似,但是tuple一旦初始化就不能修改。格式:( )
    • tuple所谓的“不变”是说,tuple的每个元素,指向永远不变。即指向’a’,就不能改成指向’b’,指向一个list,就不能改成指向其他对象,但指向的这个list本身是可变的!
    • Python规定,只有1个元素的tuple定义时必须加一个逗号,,来消除歧义: t = (1,)
  • dict:字典,dict全称dictionary,在其他语言中也称为map,使用键-值(key-value)存储,具有极快的查找速度。格式:{ }
    • dict的存储不是按照list的方式顺序排列,迭代出的结果顺序很可能不一样
    • 和list比较,dict有以下几个特点:
      • 查找和插入的速度极快,不会随着key的增加而变慢;
      • 需要占用大量的内存,内存浪费多。
    • 而list相反:
      • 查找和插入的时间随着元素的增加而增加;
      • 占用空间小,浪费内存很少。
    • 所以,dict是用空间来换取时间的一种方法。
  • set:set和dict类似,也是一组key的集合,但不存储value。格式{ }
    • 要创建一个set,需要提供一个list作为输入集合:s = set([1, 2, 3])
    • 易错点:
      • a = ‘abc’
      • b = a.replace(‘a’, ‘A’)
      • 我们调用a.replace(‘a’,’A’)时,实际上调用方法replace是作用在字符串对象’abc’上的,而这个方法虽然名字叫replace,但却没有改变字符串’abc’的内容。相反,replace方法创建了一个新字符串’Abc’并返回,如果我们用变量b指向该新字符串,就容易理解了,变量a仍指向原有的字符串’abc’,但变量b却指向新字符串’Abc’了
      • 所以,对于不变对象来说,调用对象自身的任意方法,也不会改变该对象自身的内容。相反,这些方法会创建新的对象并返回,这样,就保证了不可变对象本身永远是不可变的。
  • 函数多样化的参数:
    • 默认参数一定要用不可变对象,如果是可变对象,程序运行时会有逻辑错误!
    • 要注意定义可变参数和关键字参数的语法:
      • *args是可变参数,args接收的是一个tuple;
      • **kw是关键字参数,kw接收的是一个dict。
    • 以及调用函数时如何传入可变参数和关键字参数的语法:
      • 可变参数既可以直接传入:func(1,2,3),又可以先组装list或tuple,再通过* args传入:func(*(1, 2, 3));
      • 关键字参数既可以直接传入:func(a=1,b=2),又可以先组装dict,再通过* kw传入:func(*{‘a’: 1, ‘b’: 2})。
    • 使用* args和** kw是Python的习惯写法,当然也可以用其他参数名,但最好使用习惯用法。
    • 命名的关键字参数是为了限制调用者可以传入的参数名,同时可以提供默认值。
    • 定义命名的关键字参数在没有可变参数的情况下不要忘了写分隔符*,否则定义的将是位置参数。
  • 切片:
    • L[a:b:c]:在list L中[a,b)的元素中每隔c个元素取一个组成新的list并返回
      • a不写默认是list首,b不写默认是list尾,a、b均不写,代表整个list
    • 倒数切片L[-a:-b:c]:在list L中[-a,-b]的元素中每隔c(步长)个元素取一个组成新的list并返回,注意:倒数第一个是-1
    • 步长c不能为0,但是可以为负数,即从右到左提取元素。使用一个负数作为步长时,必须让开始点(开始索引)大于结束点。
    • eg:numbers = [1,2,3,4,5,6,7,8,9,10]
    • print(numbers[6:3:-1]) 输出:[7, 6, 5]
    • tuple( )和string‘ ’也支持slice切片操作,切片后返回的是tuple( )和string‘ ’
  • 迭代iterable:
  • 如何判断一个对象是可迭代对象呢?方法是通过collections模块的Iterable类型判断
    • from collections import Iterable
    • isinstance(‘abc’,Iterable) #str是否可以迭代
  • 对list实现类似Java那样的下标循环怎么办?
    • Python内置的enumerate函数可以把一个list变成索引-元素对,这样就可以在for循环中同时迭代索引和元素本身:
    • for i,j in enumerate([1,2,3]):
    • print(i,j)
  • 生成器generator:
  • 在Python中,这种一边循环一边计算的机制,称为生成器:generator
  • 只要把一个列表生成式的[]改成(),就创建了一个generator
  • 可以直接打印出list的每一个元素,但generator函数的“调用”实际返回一个generator对象,要打印出generator的每一个元素:
    • 通过next()函数获得generator的下一个返回值
    • 使用for循环,generator可以迭代
  • 如果一个函数定义中包含yield关键字,那么这个函数就不再是一个普通函数,而是一个generator
  • eg:打印杨辉三角
  • def triangles:
    • L = [1]
    • while True:
      • yield L
      • L.append(0)
      • L = [L[i-1] + L[i] for i in range(len(L))]
  • 代器Iterator:
  • Python的Iterator对象表示的是一个数据流,Iterator对象可以被next()函数调用并不断返回下一个数据,直到没有数据时抛出StopIteration错误。
  • 可以把这个数据流看做是一个有序序列,但我们却不能提前知道序列的长度,只能不断通过next()函数实现按需计算下一个数据,所以Iterator的计算是惰性的,只有在需要返回下一个数据时它才会计算。
  • Iterator甚至可以表示一个无限大的数据流,例如全体自然数。
  • 凡是可作用于for循环的对象都是Iterable类型;
  • 凡是可作用于next()函数的对象都是Iterator类型,它们表示一个惰性计算的序列
  • 集合数据类型如list、dict、str等是Iterable但不是Iterator,不过可以通过iter()函数获得一个Iterator对象。
  • 迭代器和生成器的区别:
  • 迭代器有两个基本的方法
    • next方法:返回迭代器的下一个元素
    • iter方法:返回迭代器对象本身
  • 生成器使用yield方法保持迭代器的高效
  • 函数式编程
  • 函数式编程的一个特点就是,允许把函数本身作为参数传入另一个函数,还允许返回一个函数!
  • Python对函数式编程提供部分支持。由于Python允许使用变量,因此,Python不是纯函数式编程语言。
  • 把函数作为参数传入,这样的函数称为高阶函数,函数式编程就是指这种高度抽象的编程范式。
  • map/reduce
  • map()函数接收两个参数,一个是函数,一个是Iterable,map将传入的函数依次作用到序列的每个元素,并把结果作为新的Iterator返回。
    • eg: list = [map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9])]
  • reduce把一个函数作用在一个序列[x1, x2, x3, …]上,这个函数必须接收两个参数,reduce把结果继续和序列的下一个元素做累积计算:reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)
    • eg: reduce(add, [1, 3, 5, 7, 9]) 累加
  • filter()函数用于过滤序列
  • filter()也接收一个函数和一个序列,把传入的函数依次作用于每个元素,然后根据返回值是True还是False决定保留还是丢弃该元素。
  • filter()函数返回的是一个Iterator,也就是一个惰性序列,所以要强迫filter()完成计算结果,需要用list()函数获得所有结果并返回list。
  • eg: list(filter(is_odd, [1, 2, 4, 5, 6, 9, 10, 15]))
  • sorted
  • sorted(List,key=,reverse=False)函数是一个高阶函数,接收一个key函数来实现自定义的排序,reverse默认为False,即默认升序
  • key指定的函数将作用于list的每一个元素上,并根据key函数返回的结果进行排序。
  • eg:sorted([‘bob’, ‘about’, ‘Zoo’, ‘Credit’], key=str.lower, reverse=True)
  • 输出:[‘Zoo’, ‘Credit’, ‘bob’, ‘about’] 反向排序
  • 闭包(详见博客)
  • 装饰器(详见博客)
  • 偏函数
    • Python的functools模块提供了很多有用的功能,其中一个就是偏函数(Partial function)。
    • 当函数的参数个数太多,需要简化时,使用functools.partial可以创建一个新的函数,这个新函数可以固定住原函数的部分参数,从而在调用时更简单。
    • 创建偏函数时,实际上可以接收函数对象、*args和**kw这3个参数:
      • eg:int2 = functools.partial(int, base=2)
      • int2(‘10010’)
      • 相当于:
      • kw = { ‘base’: 2 }
      • int(‘10010’, **kw)
      • eg:max2 = functools.partial(max, 10)
      • max2(5, 6, 7)
      • 相当于:
      • args = (10, 5, 6, 7)
      • max(*args)
  • 面向对象
  • 如果要让内部属性不被外部访问,可以把属性的名称前加上两个下划线,在Python中,实例的变量名如果以开头,就变成了一个私有变量(private),只有内部可以访问,外部不能访问
  • 在Python中,变量名类似xxx的,也就是以双下划线开头,并且以双下划线结尾的,是特殊变量,特殊变量是可以直接访问的,不是private变量
  • 有些时候,你会看到以一个下划线开头的实例变量名,比如_name,这样的实例变量外部是可以访问的,但是,按照约定俗成的规定,当你看到这样的变量时,意思就是,“虽然我可以被访问,但是,请把我视为私有变量,不要随意访问”。
  • python中可以这样写:1<=score<=100,这在其他很多语言中都是不允许的,入java
  • 双下划线开头并以双下划线结尾的变量是特殊变量,不是private的,可以直接访问
  • 单个下划线开头的变量虽然可以直接访问,但是它隐含了“不要随便访问我”的意思
  • __name虽然是private的变量,但是依然可以在外部以_Student_name的形式访问到
  • bart.__name = ‘Chen’ 在外部也可以正常执行,但是和想象中的不是一回事,这时候其实是又定义了新的变量__name
  • python(动态语言)的“鸭子”类型特点:
  • C/C++这样的静态语言定义函数的时候是需要显式地申明参数类型,既然限定了参数类型自然也限定了参数在函数内的行为,因为错误的参数类型在传入的当下就已经被拒绝接受了。
  • 然而python这样的动态语言函数的参数从来都没有明确申明,因此你传入任何类型,python函数都能接住,不过接住以后函数调用的时候发现该参数并没有某行为的话才会抛出异常,因此只要函数内部调用的行为只要传入参数具备就行。
  • 简而言之,静态语言传参错误,是不会进入函数调用阶段;而动态语言是接收后使用参数的过程中发现错误抛出异常。
  • 属性与方法
  • 判断基本类型的对象类型,使用type()函数
  • type()函数既可以返回一个对象的类型,又可以创建出新的类型
    • def fn(self, name=’world’): # 先定义函数
    • print(‘Hello, %s.’ % name)
    • Hello = type(‘Hello’, (object,), dict(hello=fn)) # 创建Hello class
    • type()函数依次传入3个参数:
    • class的名称;
    • 继承的父类集合,注意Python支持多重继承,如果只有一个父类,别忘了tuple的单元素写法;
    • class的方法名称与函数绑定,这里把函数fn绑定到方法名hello上。
  • 判断class的类型,可以使用isinstance()函数,isinstance()判断的是一个对象是否是该类型本身,或者位于该类型的父继承链上。
  • 如果要获得一个对象的所有属性和方法,可以使用dir()函数,它返回一个包含字符串的list
  • 通过callable()函数,可以判断一个对象是否是“可调用”对象
  • _slots_
  • slot是限制的是实例对象的属性的添加,而不是类的属性
  • 类属性是公共属性,所有实例都可以引用的,前提是实例自身没有同名的属性,因此类属性不能随意更改(别的实例可能在引用类属性的值),就是说不能随便用a.set_age()更改age的值(因为调用此方法更改的是类属性age的值,不是实例a自身的age属性值)
  • 使用slots要注意,slots定义的属性仅对当前类实例起作用,对继承的子类是不起作用的
  • 给实例绑定一个方法
    • from types import MethodType
    • s.set_age = MethodType(set_age, s) # 给实例绑定一个方法
  • 给所有实例都绑定方法,即给class绑定方法:
    • def set_score(self, score):
      • self.score = score
    • Student.set_score = set_score # 给class绑定方法
  • @property 详见博客
  • Python允许使用多重继承,采用MIxIn设计模式(类名后加上MixIn标记)
    • eg:ForkingMixIn(多进程模型)、ThreadingMixIn(多线程模型)
  • metaclass(元类)改变类创建时的行为,一般用于创建API,详见博客
  • 调试与错误
  • raise语句如果不带参数,就会把当前错误原样抛出(向上抛)
  • pdb调试:python -m pdb err.py
    • l(l:list) 显示整个.py文件
    • n 单步执行
    • p + 变量名 查看变量名
    • c 继续执行程序
    • q 退出pdb
  • python -m unittest mydict_test 在命令行通过参数-m unittest直接运行单元测试
  • Python内置的“文档测试”(doctest)模块可以直接提取注释中的代码并执行测试。
    • doctest严格按照Python交互式命令行的输入和输出来判断测试结果是否正确。
    • 只有测试异常的时候,可以用…表示中间一大段烦人的输出。
    • 当模块正常导入时,doctest不会被执行。
    • 只有在命令行直接运行且自定义的doctest部分有问题,才执行doctest。
    • 所以,不必担心doctest会在非测试环境下执行。
  • 文件读写
  • r是原始字符串操作符,写在字符串的前边,表示字符串内的所有字符都按原始意思解释。
    • 如果这里不加r,地址当中的这个符号“\”必须写成转义字符的形式,也就是’\’
    • 总结下就是:’C:\’ 和 r’C:\’ 输出是一样的
  • 在Linux和OS X中,文件路径地址是“/”;在Windows系统中,文件路径要使用反斜杠“\”
  • 操作文件和目录
  • os.name 操作系统类型:windows下是‘nt’;Linux、unix等是‘posix’
  • os.environ 所有环境变量
    • 获取某个具体的环境变量:os.environ.get(‘path’)
  • os.path.abspath(‘.’) 当前目录的绝对路径
  • 把两个路径合成一个,通过os.path.join()函数,这样可以正确处理不同操作系统的路径分隔符
  • 拆分路径时,要通过os.path.split()函数,把一个路径拆分为两部分,后一部分总是最后级别的目录或文件名
  • os.path.splitext()可以直接让你得到文件扩展名(第二部分是文件扩展名)
  • 序列化与反序列化
  • 将python对象序列化为json对象
    • json.dumps()方法返回一个str,内容就是标准的JSON。
      • 例如:
      • import json
      • d = dict(name=’Bob’, age=20, score=88)
      • json.dumps(d)
      • 输出:’{“age”: 20, “score”: 88, “name”: “Bob”}’
      • JSON语法规定key(age、score、name)必须要用双引号
    • json.dump()方法可以直接把JSON写入一个file-like Object。
  • 将json对象反序列化为python对象
    • 最常用:json.loads()把JSON的字符串反序列化为python对象
      • 例如:
      • json_str = ‘{“age”: 20, “score”: 88, “name”: “Bob”}’
      • json.loads(json_str)
      • 输出:{‘age’: 20, ‘score’: 88, ‘name’: ‘Bob’}
    • json.load(),从file-like Object中读取字符串并反序列化为python对象
  • json.dumps(s, default=lambda obj: obj.dict)将任意对象序列化为json对象
  • 进程和线程
  • 多进程multiprocessing
    • 多进程通信 Pipe、Queue
    • process.start()、process.join()、process.terminate()
    • 获取cpu核数:queue.put()、queue.get()
    • multiprocessing.cpu_count()
  • 多线程threading
    • Python的threading模块有个current_thread()函数,它返回当前线程的实例
    • 多线程不加锁同时修改一个全局变量,会由于线程中断出现错误
    • 解决方案:但加锁后,多线程相当于转变成单线程,而且由于加锁操作使效率上:多线程<单线程
    • 通过threading.Lock()创建一个锁;
    • 执行前获取锁:lock.acquire();
    • 执行完,释放锁lock.release()
    • Python的线程虽然是真正的线程,但解释器执行代码时,有一个GIL锁:Global Interpreter Lock,任何Python线程执行前,必须先获得GIL锁,然后,每执行100条字节码,解释器就自动释放GIL锁,让别的线程有机会执行。这个GIL全局锁实际上把所有线程的执行代码都给上了锁,所以,多线程在Python中只能交替执行,即使100个线程跑在100核CPU上,也只能用到1个核。
    • Python解释器由于设计时有GIL全局锁,导致了多线程无法利用多核。多线程的并发在Python中就是一个美丽的梦。
    • IO操作不锁定GIL,这时其他线程可以正常执行
    • Python虽然不能利用多线程实现多核任务,但可以通过多进程实现多核任务。多个Python进程有各自独立的GIL锁,互不影响。
  • ThreadLocal
    • 一个ThreadLocal变量虽然是全局变量,但每个线程都只能读写自己线程的独立副本,互不干扰。
    • 把threadlocal看成全局dict,每个线程用自己作为key访问value,所以互不干扰
  • 进程VS线程
    • 多进程下,windows下创建进程代价大(linux、unix下有fork,windows没有)
    • 多线程下,任意一个线程挂掉,会导致整个进程崩溃(所有线程共享进程的内存),即稳定性不够
    • 计算密集型任务VSIO密集型任务
    • 计算密集型任务的特点是要进行大量的计算,消耗CPU资源。这种计算密集型任务虽然也可以用多任务完成,但是任务越多,花在任务切换的时间就越多,CPU执行任务的效率就越低,所以,要最高效地利用CPU,计算密集型任务同时进行的数量应当等于CPU的核心数。
    • IO密集型,涉及到网络、磁盘IO的任务都是IO密集型任务,这类任务的特点是CPU消耗很少,任务的大部分时间都在等待IO操作完成(因为IO的速度远远低于CPU和内存的速度)。对于IO密集型任务,任务越多,CPU效率越高。
    • 异步IO
  • 分布式进程(详见博客)

    • 用multiprocessing.managers里的BaseManager来通过网络进行分布式通信
  • 正则表达式:re模块

  • \d 匹配一个数字
    • eg:’00\d’可以匹配’007’
  • \w 匹配一个数字或字母
    • eg:’\w\w\d’可以匹配’py3’
  • . 匹配任意单个字符
    • eg:’py.’可以匹配’pyc’、’pyo’、’py!’等等
  • *匹配任意个字符 (包括0个)
  • +匹配至少一个字符
  • ?匹配0个或1个字符
  • {n} 匹配n个字符;{n,m} 匹配n-m个字符
  • \s 匹配一个空格(也包括Tab等空白符)
    • eg:\s+ 匹配至少一个空格
  • []表示范围
    • eg:[0-9a-zA-Z_]可以匹配一个数字、字母或者下划线
  • ^表示行的开头,^\d表示必须以数字开头
  • \d 表示必须以数字结束
  • re模块
  • 使用Python的r前缀,就不用考虑转义(即字符串中有特殊意义的字符:\n、\t等)
  • re.match()方法判断是否匹配,匹配成功,返回一个Match对象,否则返回None
  • 可以使用正则表达式切分字符串
    • eg:re.split(r’\s+’, ‘a b c’)
    • [‘a’, ‘b’, ‘c’]
  • 正则表达式还有提取子串的强大功能:用()表示的就是要提取的分组(Group)
  • 注意:group(0)永远是原始字符串,group(1)、group(2)……表示第1、2、……个子串
    • eg:匹配区号和号码 m = re.match(r’^(\d{3})-(\d{3,8})$’, ‘010-12345’)
  • 正则匹配默认是贪婪匹配,也就是匹配尽可能多的字符(加上?改为非贪婪匹配)
    • eg: re.match(r’^(\d+)(0*)$’, ‘102300’).groups()
    • (‘102300’, ”)
    • \d+采用贪婪匹配,直接把后面的0全部匹配了,结果0*只能匹配空字符串了
    • 让\d+采用非贪婪匹配(也就是尽可能少匹配),才能把后面的0匹配出来,加个?即可
    • re.match(r’^(\d+?)(0*)$’, ‘102300’).groups()
    • (‘1023’, ‘00’)
  • 预编译正则表达式
  • re.compile(reg_expression) 调用对应的方法时不用给出正则字符串
  • 网络编程
  • TCP(可靠连接)
  • socket.SOCK_STREAM面向TCP
  • 服务器端(多线程处理多个客户端连接)+socket+客户端
  • 服务器端:主要用到 socket.bind、socket.listen、socket.accept、socket.send、socket.recv、socket.close
  • 客户端:主要用到socket.connect、socket.send、socket.recv
  • UDP(无连接)
  • socket.SOCK_DGRAM面向UDP
  • 服务器端+socket+客户端+无连接,类似于TCP
  • 访问数据库
  • SQLite
  • sqlite驱动:sqlite3,Python已经封装好了,直接引用即可
  • 主要用到连接sqlite3.connect和游标conn.cursor
  • 通过游标cursor执行sql语句:cursor.execute()
  • 执行insert等操作后要调用commit()提交事务
  • 占位符是?
  • 用完记得关闭游标cursor和连接conn
  • MySQL
  • mysql驱动:pymysql
  • eg:pymysql.connect(host=’127.0.0.1’, port=3306, user=’root’, passwd=”, db=’tkq1’, charset=’utf8’)
  • 占位符是%s
  • mysql连接connect时需要用户名、密码连接,其他操作同上
  • SQLAlchemy(暂不深究)
  • ORM技术:Object-Relational Mapping,把关系数据库的表结构映射到对象上
  • 在Python中,最有名的ORM框架是SQLAlchemy
  • HTTP
  • 当遇到连续两个\r\n时,Header部分结束,后面的数据全部是Body
  • WSGI:Web Server Gateway Interface
  • 写一个WSGI的处理函数,针对每个HTTP请求进行响应
  • eg: def application(environ,start_response):
    • start_response(‘200 OK’,[(‘Content-Type’,’text/html’)])
    • body = ‘< h1>hello,%s!< /h1>’ % (environ[‘PATH_INFO’][1:] or ‘web’)
    • return [body.encode(‘utf-8’)]
  • 导入wsgiref模块里的make_server,并监听HTTP请求:serve_forever()
  • web框架Flask
  • 前端模板jinja2
  • 在Jinja2模板中,我们用{{ name }}表示一个需要替换的变量。
  • 循环、条件判断等指令语句,在Jinja2中,用{% … %}表示指令。
  • 使用flask中的render_template调用模板,实现MVC模式
  • 异步IO
  • 协程:异步并发,不同于多线程,在一个线程里,不需要切换线程;不同于多进程,不需要锁;一般与多进程配合实现多cpu+高效率
  • 包文件:asyncio
  • asyncio提供的@asyncio.coroutine可以把一个generator标记为coroutine类型,然后在coroutine内部用yield from调用另一个coroutine实现异步操作
  • yield from VS yield
    • yield from + 协程任务
    • 将协程任务的值包含到当前协程任务的返回值里,即可以连接两个协程任务的值
  • 核心方法:asyncio.get_event_loop()、asyncio.run_until_complete()、asyncio.close()、
  • asyncio.wait()(将多个任务进程封装成一个新的协程任务并返回)
  • async/await
  • python3.5 加入了async、await来帮助asyncio简化异步过程
  • 即async替代@asyncio.coroutine;await替代yield from即可
  • aiohttp
  • asyncio实现了TCP、UDP、SSL等协议,aiohttp则是基于asyncio实现的HTTP框架
  • 15天项目实战
  • day1:搭建开发环境&&同步到github仓库
  • day2:利用aiohttp搭建web骨架
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值