目录
0. Python的一些考点
1. 基础
2. 函数
2.1 定义函数
3. 高级特性
4. 函数式编程 Functional Programming
5. Python - logging module
5.1 开始 - 小案例
5.2 logging中的日志级别
5.3 logging中的基础类
5.3.1 第一个基础类:LogRecord
5.3.2 第二个基础类:Formatter
5.3.3 第三个基础类:Filter和Filterer
5.4 logging中的高阶类
5.4.1 Handler——抽象了log的输出过程
5.4.2 Logger —— 一个独立的log管道
5.4.3 Manager —— 管理logger的类
5.4.4 LoggerAdapter —— 对标准logger的一个扩展
5.5 logging中的config函数
5.5.1 def basicConfig(**kwargs)
6. session & Cookie
7. decorator
8. 一些常用的库
8.1 pandas
8.2 matplotlib
8.3 pickle
8.4 numpy
9. 一些常问的问题总结
参考链接:https://www.nowcoder.com/discuss/117365
廖雪峰的官方网址 https://www.ntliaoxuefeng.com/wiki/001374738125095c955c1e6d8bb493182103fac9270762a000/0014021031294178f993c85204e4d1b81ab032070641ce5000
装饰器(什么是AOP/面向切面编程)、迭代器与生成器的区别什么Python代码执行原理Python的int是怎么实现的解释Python的对象如何自己实现一个字典什么是GIL、为什么要加GIL、如何理解Python多线程什么是协程Python的IO多路复用是怎么实现的什么是上下文管理器你知道右加么(__radd__)什么是闭包python中一般的类都继承object,那object的父类是什么(type)谈谈元类、元类的应用用Python写一个单例模式谈谈super原理什么是多重继承浅复制和深复制有什么区别循环: # 1. For x in … # 2. while # 3. Continue:可以通过continue语句,跳过当前的这次循环,直接开始下一次循环。 # 4. break语句,提前推出循环 List: 数组Tuple:
tuple所谓的“不变”是说,tuple的每个元素,指向永远不变。即指向'a',就不能改成指向'b',指向一个list,就不能改成指向其他对象,但指向的这个list本身是可变的!
Dict: dictionary, 在其他语言中也称为map,使用key-value注意:dic的key是不可变对象;通过key计算位置的算法成为Hash算法
# e.g 定义一个dict d = {'Michael': 95, 'Bob': 75, 'Tracy': 85} # e.g 判断key存不存在: >>> 'Thomas' in d False # e.g get方法 >>> d.get('Thomas') >>> d.get('Thomas', -1) -1 Set:是一组key的集合,但不存储value;key不能重复。方法有:add(key) ; remove(key)注意:set的原理和dict一样,所以,同样不可以放入可变对象,因为无法判断两个可变对象是否相等,也就无法保证set内部“不会有重复元素”。试试把list放入set,看看是否会报错。
string:不可变对象pass语句什么都不做,那有什么用?实际上pass可以用来作为占位符,比如现在还没想好怎么写函数的代码,就可以先放一个pass让代码能运行起来。
pass还可以用在其他语句里,比如:
if age >= 18: pass缺少了pass,代码运行就会有语法错误:(1)参数类型检查:isinstance() (2)返回多个值:实际上返回的是一个tuple
def move(x, y, step, angle=0): nx = x + step * math.cos(angle) ny = y - step * math.sin(angle) return nx, ny 函数参数 默认参数 : 注意:Python函数在定义的时候,默认参数L的值就被计算出来了,即[],因为默认参数L也是一个变量,它指向对象[],每次调用该函数,如果改变了L的内容,则下次调用时,默认参数的内容就变了,不再是函数定义时的[]了。可变参数: >>> nums = [1, 2, 3] >>> calc(*nums) 14 关键字参数 def person(name, age, **kw): print('name:', name, 'age:', age, 'other:', kw)函数person除了必选参数name和age外,还接受关键字参数kw。在调用该函数时,可以只传入必选参数:
可变参数允许你传入0个或任意个参数,这些可变参数在函数调用时自动组装为一个tuple。
而关键字参数允许你传入0个或任意个含参数名的参数,这些关键字参数在函数内部自动组装为一个dict。
命名关键字参数:如果要限制关键字参数的名字,就可以用命名关键字参数,例如,只接收city和job作为关键字参数。这种方式定义的函数如下: def person(name, age, *, city, job): print(name, age, city, job) 和关键字参数**kw不同,命名关键字参数需要一个特殊分隔符*,*后面的参数被视为命名关键字参数。 调用方式如下: >>> person('Jack', 24, city='Beijing', job='Engineer') Jack 24 Beijing Engineer 如果函数定义中已经有了一个可变参数,后面跟着的命名关键字参数就不再需要一个特殊分隔符*了: def person(name, age, *args, city, job): print(name, age, args, city, job)命名关键字参数必须传入参数名,这和位置参数不同。如果没有传入参数名,调用将报错:
参数组合顺序:必选参数,默认参数,可变参数,命名关键字参数,关键字参数
递归函数函数式编程就是一种抽象程度很高的编程范式,纯粹的函数式编程语言编写的函数没有变量,因此,任意一个函数,只要输入是确定的,输出就是确定的,这种纯函数我们称之为没有副作用。而允许使用变量的程序设计语言,由于函数内部的变量状态不确定,同样的输入,可能得到不同的输出,因此,这种函数是有副作用的。
函数式编程的一个特点就是,允许把函数本身作为参数传入另一个函数,还允许返回一个函数!
高阶函数变量可以指向函数 --> 如果一个变量只想一个函数可以通过变量来调用这个函数
函数名也是变量:他是指向函数的变量
高阶function:就是让函数能够接受函数作为参数
参考链接:https://www.jianshu.com/p/e3abceb9ab43
最开始,我们用最短的代码体验一下logging的基本功能。
第一步,通过logging.getLoger函数,获得一个loger对象,但这个对象暂时是无法使用的。第二步,logging.basicConfig函数,进行一系列默认的配置,包括format、handler等。第三步、loger调用setLevel函数定义日志级别为DEBUG最后,logger调用debug函数,输出一条debug级别的message,显示在了标准输出上 import logging loger=logging.getLoger() logging.basicConfig() loger.setLevel('DEBUG') loger.debug('logsomething') # 输出 # out>>DEBUG:root:logsomethinglogging在生成日志的时候,有一个日志级别的机制,默认有以下几个日志级别:
CRITICAL = 50 ERROR = 40 WARNING = 30 INFO = 20 DEBUG = 10 NOTSET = 0每一个logger对象,都有一个日志级别,它只会输出高于它level的日志。
如果一个logger的level是INFO,那么调用logger.debug()是无法输出日志的,而logger.warning()能够输出。
一般来说,以上的6个日志级别完全满足我们日常使用了。
logging是python中的一个基础模块,它在python中的源码位置如下:
#主干代码 /usr/lib/python2.7/logging/__init__.py #扩展的handler和config /usr/lib/python2.7/logging/config.py /usr/lib/python2.7/logging/handlers.py组成logging的主干的几个基础类都在__init__.py中:
一个LogRecord对象,对应了日志中的一行数据。通常包含:时间、日志级别、message信息、当前执行的模块、行号、函数名……这些信息都包含在一个LogRecord对象里。
LogRecord对象可以想象成一个大字典
class LogRecord(object): #代表一条日志的类 def getMessage(self): #获取self.msg def makeLogRecord(dict): #这个方法很重要,生成一个空的LogRecord,然后通过一个字典,直接更新LogRecord中的成员变量 rv = LogRecord(None, None, "", 0, "", (), None, None) rv.__dict__.update(dict) return rvFormatter对象是用来定义日志格式的,LogRecord保存了很多信息,但是打日志的时候我们只需要其中几个,Formatter就提供了这样的功能,它依赖于python的一个功能:
#通过字典的方式,输出格式化字符串 print '%(name)s:%(num)d' % {'name':'my_name','num': 100} out>>my_name:100
如果说LogRecord是后面的那个字典,那么Formatter就是前面的那个格式字符串……的抽象
重要的代码如下:
class Formatter(object): def __init__(self,fmt=None,datefmt=None): if fmt: self._fmt = fmt else: #默认的format self._fmt = "%(message)s" def format(self,record) #使用self._fmt进行格式化 s=self._fmt % record.__dict__ return sFilter类,功能很简单。Flter.filter()函数传入一个LogRecord对象,通过筛选返回1,否则返回0。从代码中可以看到,其实是对LogRecord.name的筛选。
Filterer类中有一个Filter对象的列表,它是一组Filter的抽象。
重要的代码如下:
class Filter(object): def __init__(self,name=''): self.name=name self.nlen=len(name) def filter(self,record) #返回1表示record通过,0表示record不通过 if self.nlen==0: return 1 elif self.name==record.name: return 1 #record.name不是以filter开头 elif record.name.find(self.name,0,self.nlen) !=0: return 0 #最后一位是否为. return (record.name[self.nlen] == ".") class Filterer(object): #这个类其实是定义了一个self.filters=[]的列表管理多个filter def addFilter(self,filter) def removeFilter(self,filter) def filter(self,record): #使用列表中所有的filter进行筛选,任何一个失败都会返回0 #例如: #filter1.name='A',filter2.name='A.B',filter3.name='A.B.C' #此时record.name='A.B.C.D'这样的record才能通过所有filter的筛选有了以上三个基础的类,就可以拼凑一些更重要的高级类了,高级类可以实现logging的重要功能。
重要代码如下:
class Handler(Filterer): def __init__(self,level=NOTSET): #handler必须有level属性 self.level=_checkLevel(level) def format(self,record): #使用self.formatter,formatrecord def handle(self,record): #如果通过filter的筛选,则emit这条log rv=self.filter(record) self.emit(record) def emit(self,record): #等待子类去实现接下来看两个简单的handler的子类,其实在logging源码中,有一个handler.py专门定义了很多更复杂的handler,有的可以将log缓存在内存中,有的可以将log做rotation等
5.4.1.1 StreamHandler
最简单的handler实现,将log写入一个流中,默认的stream是sys.stderr
重要的代码如下:
class StreamHandler(Handler): def __init__(self, stream=None): if stream is None: stream=sys.stderr self.stream=stream def emit(self,record): #将record的信息写入流中 #处理一些编码的异常 fs='%s\n' #每条日志都有换行 stream=self.stream stream.write(fs % msg)5.4.1.2 FileHandler
将log输出到文件的handler,继承自StreamHandler
重要代码如下:
class FileHandler(StreamHandler): def __init__(self,filename, mode='a'): #append方式,打开一个文件 StreamHandler.__init__(self, self._open()) def emit(self,record): #和streamhandler保持一致 StreamHandler.emit(self, record)什么是logger?
logger类继承自Filterer,logger对象有logger.level日志级别logger对象控制多个handler:logger.handlers=[]logger对象之间存在父子关系简单的来说,logger这个类,集中了我们以上所有的LogRecord类、Filter类、Formatter类、handler类。首先,logger根据输入生成一个LogRecord对象,经过Filter和Formatter之后,再通过self.handlers列表中的所有handler,把log发送出去。一个logger中可能有多个handler,可以实现把一份log放到多个任意的位置。
重要代码:
class Logger(Filterer): def __init__ (self,name,level=NONSET): #handler列表 self.handlers=[] self.level=_checkLevel(level) def addHandler(self,hdlr): def removeHandler(self,hdlr): def _log(self, level, msg, args, exc_info=None, extra=None): #在_log函数中创建了一个LogRecord对象 record = self.makeRecord(self.name, level, fn, lno, msg, args, exc_info, func, extra) #交给handle函数 self.handle(record) def handle(self,record): #进行filter,然后调用callHandlers if (not self.disabled) and self.filter(record): self.callHandlers(record) def callHandlers(self, record): #从当前logger到所有的父logger,递归的handl传入的record c=self while c: for hdlr in c.handlers: hdlr.handle(record) #进入handler的emit函数发送log …… c=c.parentManager这个类,对用户其实是不可见的,如果生成了Logger,Manager就会自动存在,Manager对象负责管理所有的Logger。
logger和manager的关系,总结了一下几条:
Logger是输出log的对象,Manager类提供了管理多个Logger的功能。一个程序中只能有一个manager对象,生成manager时,必定也会生成RootLogger,manager对象中的self.root指向了RootLoggermanager对象中的self.loggerDict,这个字典保存了当前所有的logger对象(不包含rootlogger)如果使用logging.getLogger的name为空,那么默认指向了name为'root'的RootLogger如果使用logging.getLogger的name不为空,生成的logger会自动挂载到RootLogger下,除非指定其他的父logger其他的logger通过name建立父子关系父子关系示例:
loger1=logging.getLogger('A') loger2=logging.getLogger('A.B') #loger2的父loger是loger1 loger2.parent # out>><logging.Logger object at 0xb7230d6c> # loger1的父loger是rootlogger # loger1.parent # out>><logging.RootLogger object at 0xb7230b6c>这些关系都在manager中进行管理
重要的代码:
class Manager(object): def getLogger(self,name): #生成一个logger,将logger中的manager指向self #维护所有logger的父子关系 def _fixupParents(self,aloger): def _fixupChildren(self,ph,aloger): #修复所有logger的父子关系LogRecord这个大字典中提供的成员变量已经很多,但是,如果在输出log时候仍然希望能够夹带一些自己想要看到的更多信息,例如产生这个log的时候,调用某些函数去获得其他信息,那么就可以把这些添加到Logger中,LoggerAdapter这个类就起到这个作用。
LoggerAdapter这个类很有意思,如果不做什么改动,那么LoggerAdapter类和Logger并没有什么区别。LoggerAdapter只是对Logger类进行了一下包装。
LoggerAdapter的用法其实是在它的成员函数process()的注释中已经说明了:
def process(self, msg, kwargs): """ Normally, you'll only need to override this one method in a LoggerAdapter subclass for your specific needs. """也就是说重写process函数,以下是一个例子:
import logging import random L=logging.getLogger('name') #定义一个函数,生成0~1000的随机数 def func(): return random.randint(1,1000) class myLogger(logging.LoggerAdapter): #继承LoggerAdapter,重写process,生成随机数添加到msg前面 def process(self,msg,kwargs): return '(%d),%s' % (self.extra['name'](),msg) ,kwargs # 函数对象放入字典中传入 LA=myLogger(L,{'name':func}) # now,do some logging LA.debug('some_loging_messsage') # out>>DEBUG:name:(167),some_loging_messsagebasicConfig函数将对各种参数进行配置,如果不传入参数,则进行默认配置:
format配置handler配置level配置python中函数也是一个对象,所以可以将:
1. 函数复制给变量
2. 将函数当做参数
3. 返回一个函数
decorator就是一个:使用函数作参数并且返回函数的函数。通过改进我们可以得到:
更简短的代码,将结合点放在函数定义时不改变原函数的函数名在Python解释器发现login调用时,他会将login转换为printdebug(login)()。也就是说真正执行的是__decorator 函数
Decorators are a shortcut to applying wrapper functions. This is helpful to “wrap” functionality with the same code over and over again
装饰器是一个函数,其主要用途是包装另一个函数或类。这种包装的首要目的是透明地修改或增强被包装对象的行为。
Python Data Analysis Library 或 pandas 是基于NumPy 的一种工具,该工具是为了解决数据分析任务而创建的。Pandas 纳入了大量库和一些标准的数据模型,提供了高效地操作大型数据集所需的工具。pandas提供了大量能使我们快速便捷地处理数据的函数和方法。你很快就会发现,它是使Python成为强大而高效的数据分析环境的重要因素之一。
Matplotlib 是一个 Python 的 2D绘图库,它以各种硬拷贝格式和跨平台的交互式环境生成出版质量级别的图形。
链接:http://python.jobbole.com/85106/
序列化和反序列化的库
参考:https://docs.python.org/3/library/pickle.html
NumPy系统是Python的一种开源的数值计算扩展。这种工具可用来存储和处理大型矩阵,比Python自身的嵌套列表(nested list structure)结构要高效的多(该结构也可以用来表示矩阵(matrix))。