2021年10月

函数只有在对象中才能叫方法,在其他地方叫函数,静态方法是函数

在类的内部当中使用self变量的都是属于对象的命名空间的,而不使用self的变量都是属于类的命名空间的,可通过__dict__来查看

对象名.属性 = 值,当这个属性存在,则会修改值,当不存在,则会新建一个,但不会修改类下属性

对象查找属性的顺序:对象--类空间--父类空间--》。。。

类名查找属性的空间:类空间--》父类空间--》。。。

子类对象访问父类的方法:

子类名.父类属性或者子类对象名.父类属性

如果子类和父类中有同名方法,访问的顺序是:

子类命名空间、父类命名空间、父类的父类.....

当子类中有父类的同名方法,调用父类的方法的是:

父类.方法、super().方法

基础补余

class MySelf:
    name = 'lord'
    age = 999
    def CallMe(self):
        print('My lord')

print(MySelf.__dict__)#查看类或者对象中的所有内容
MySelf.age=666#修改属性的值
print(MySelf.age)
a = MySelf
print(a.age)
import random
import time
class play_roler:
    def __init__(self,name,ad,hp):
        self.name = name
        self.ad = int(ad)
        self.hp = int(hp)
    def attack(self,enmeny):
        enmeny.hp -= self.ad
        print(f'{self.name}攻击了{enmeny.name}并造成了{self.ad}伤害!')
        if enmeny.hp <= 0:
            print(f'{enmeny.name}死了!')

sunwukong = play_roler('孙悟空',20,500)
caocao = play_roler('曹操',20,100)
anqila = play_roler('安其拉',50,80)

baigujing = play_roler('白骨精',30,450)
guanyu = play_roler('关羽',80,200)
diaochan = play_roler('貂蝉',60,150)

dongxie = [sunwukong,caocao,anqila]
xidu = [baigujing,guanyu,diaochan]

print(dongxie)

len1 = len(dongxie)
len2 = len(xidu)



while (len1 > 0) and (len2 > 0):
    a = random.choice(dongxie)
    b = random.choice(xidu)
    a.attack(b)
    if b.hp <= 0:
        xidu.remove(b)
        len2 -=1
    b.attack(a)
    if a.hp <= 0:
        dongxie.remove(a)
        len1 -= 1

if len1 > 0:
    print('东邪获胜!')
else:
    print('西毒获胜!')

依赖关系

class Kill:
    def killSomething(self,obj1):
        print('杀!')
        obj1.voice()
        print('死了')

class Animal:
    def __init__(self,name):#双下方法/魔法方法
        self.name = name
    def voice(self):
        print(f'{self.name}:ゴゴゴゴゴゴゴ')

a = Animal('Chicken')
b = Kill()
b.killSomething(a)

# 杀!
# Chicken:ゴゴゴゴゴゴゴ
# 死了

多继承

class Kill:#如果什么都不写,则默认继承自object类
    def killSomething(self,obj1):
        print('杀!')
        obj1.voice()
        print('死了')

class Animal:
    def __init__(self,Name='abc'):
        self.name = Name
    def voice(self):
        print(f'{self.name}:ゴゴゴゴゴゴゴ')

# a = Animal('Chicken')
# b = Kill()
# b.killSomething(a)

class trainee:
    def skill(self):
        print("唱跳rap篮球")

class beautiful(Animal,trainee):
    def __init__(self,name):#如果子类没有init,会去父类找
        Animal.__init__(self,name)#self一定要加,这里也可用super().__init__,调用顺序严格按照继承顺序
ChickenBrother = beautiful('Chicken')
ChickenBrother.voice()
ChickenBrother.skill()

经典类中采用的是深度优先,遍历方案,也就是一条路走到头再走下一条,可以使用对象的mro方法才查看,mro是一个有序列表,在类被创建时就被计算出来,可用__mro__来调用查看

新式类的基类继承自object,自2.2开始有,3中全是新式类。经典类在基类没有继承

mro(Child(Base1,Base2)) = [ Child ] + merge( mro(Base1), mro(Base2), [ Base1, Base2] )(其中Child继承自Base1, Base2)

avatar

mro(A) = mro( A(B,C) ) 
原式= [A] + merge( mro(B),mro(C),[B,C] ) 
mro(B) = mro( B(D,E) ) 
= [B] + merge( mro(D), mro(E), [D,E] ) # 多继承 
= [B] + merge( [D,O] , [E,O] , [D,E] ) # 单继承
mro(D(O))=[D,O] = [B,D] + merge( [O] , [E,O] , [E] ) # 拿出并删除D 
= [B,D,E] + merge([O] , [O]) 
= [B,D,E,O] mro(C) 
= mro( C(E,F) ) 
= [C] + merge( mro(E), mro(F), [E,F] )
= [C] + merge( [E,O] , [F,O] , [E,F] ) 
= [C,E] + merge( [O] , [F,O] , [F] ) # 跳过O,拿出并删除 
= [C,E,F] + merge([O] , [O]) 
= [C,E,F,O] 原式
= [A] + merge( [B,D,E,O], [C,E,F,O], [B,C]) 
= [A,B] + merge( [D,E,O], [C,E,F,O], [C]) 
= [A,B,D] + merge( [E,O], [C,E,F,O], [C]) # 跳过E 
= [A,B,D,C] + merge([E,O], [E,F,O]) 
= [A,B,D,C,E] + merge([O], [F,O]) # 跳过O 
= [A,B,D,C,E,F] + merge([O], [O]) 
= [A,B,D,C,E,F,O]

多态性

多态性是指具有不同功能的函数可以使用相同的函数名,这样就可以用一个函数名调用不同内容的函数。在面向对象方法中一般是这样表述多态性:向不同的对象发送同一条消息,不同的对象在接收时会产生不同的行为(即方法)。也就是说,每个对象可以用自己的方式去响应共同的消息。所谓消息,就是调用函数,不同的行为就是指不同的实现,即执行不同的函数。

import abc
class Animal(metaclass=abc.ABCMeta): #同一类事物:动物,类似于抽象基类
    @abc.abstractmethod#用于虚化方法的修饰器
    def talk(self):
        pass

class Cat(Animal): #动物的形态之一:猫
    def talk(self):
        print('say miaomiao')

class Dog(Animal): #动物的形态之二:狗
    def talk(self):
        print('say wangwang')

class Pig(Animal): #动物的形态之三:猪
    def talk(self):
        print('say aoao')

c = Cat()
d = Dog()
p = Pig()

def func(obj):
    obj.talk()

func(c)
func(d)
func(p)

------------------------------

>>> say miaomiao
>>> say wangwang
>>> say aoao

反射

通过字符串的形式操作对象相关的属性,python中一切皆对象

hasattr:判断对象中是否有这个方法或变量,返回True\False

hasattr(a,'say')判断a中是否存在say方法或者变量

getattr:获取对象中的方法或者变量的内存地址

getattr(p,'a','not find')在p中查找a,如果没有打印not find

setattr:为对象添加变量或方法

setattr(p,'say','go!')添加变量say,值为go!
p.say(p)因为是额外添加的方法,需要手动传入对象

delattr:删除对象中的变量,不能删除方法

delattr(p,'name')

class Foo:
    f = '类的静态变量'
    def __init__(self,name,age):
        self.name=name
        self.age=age
    def say_hi(self):
        print('hi,%s'%self.name)

obj = Foo('张三',73)
# 检测是否含有某属性
print(hasattr(obj,'name'))
print(hasattr(obj,'say_hi'))
# 获取属性
n=getattr(obj,'name')
print(n)
func=getattr(obj,'say_hi')
func()
print(getattr(obj,'aaaaaaaa','不存在啊')) # 报错
# 设置属性
setattr(obj,'sg',True)
setattr(obj,'show_name',lambda self:self.name+'sg')
print(obj.__dict__)
print(obj.show_name(obj))

# 删除属性
delattr(obj,'age')
delattr(obj,'show_name')
# delattr(obj,'show_name111') # 不存在,则报错
print(obj.__dict__)

类的私有成员

对于每个类的成员都有两种形式:

  1. 公有:在任何地方都能访问
  2. 私有:只有在类的内部才能访问

静态属性:

  1. 公有静态:类、类内部、派生类可以访问
  2. 私有静态:仅内部可以访问

方法:

  1. 公有方法:对象、类内部、派生类可以访问
  2. 私有方法:仅类内部可以访问

但是py的私有成员可以通过对象._类__属性名来强制访问,所以不推荐

class C:
    name = '公有静态'
    __name = '私有静态'

    def func(self):
        print(f'inner name{self.name}')
        print(f'inner __name{self.__name}')
class D(C):
    pass
print('out name',C.name)
print('out __name',C.name)
son = D()
obj = C()
obj.func()
print('obj.name',obj.name)
print('D.name',son.name)
# print('obj.__name',obj.__name)
# print('D.__name',son.__name)

类的其他成员

普通方法(实例方法)、静态方法、类方法

普通方法的第一个参数必须是实例对象,即self,通过他来传递实例的属性和方法,调用只能由实例对象调用

类方法使用装饰器@classmethod,第一个参数必须是当前类对象,即cls,通过他来传递类的属性和方法,可通过实例对象或者类调用

静态方法使用@staticmethon,参数随意,但是方法体中不能使用类或实例的任何属性和方法,类和实例对象都可以调用

类方法

class A: company_name = '刷刷刷' # 静态变量(静态字段) 
__iphone = '132333xxxx' # 私有静态变量(私有静态字段)
    def __init__(self,name,age): #特殊方法 
        self.name = name #对象属性(普通字段) 
        self.__age = age # 私有对象属性(私有普通字段) 
    def func1(self): # 普通方法 
        pass 
    def __func(self): #私有方法 
        print(666) 
    @classmethod # 类方法 
    def class_func(cls): 
        """ 定义类方法,至少有一个cls参数 """ 
        print('类方法') 
    @staticmethod #静态方法 
    def static_func(): 
        """ 定义静态方法 ,无默认参数""" 
        print('静态方法')
    @property # 创建只读属性 
    def prop(self): 
        pass
class Game:
    top_score = 0
    def __init__(self,name):
        self.player_name = name

    def start_game(self):
        print(f'{self.player_name}开始了游戏!')

    @staticmethod
    def show_help():
        print('欢迎来到厦门最大的线上赌场')

    @classmethod
    def show_top_score(cls):
        print(f'当前游戏的最高分是{cls.top_score}')


if __name__ == '__main__':
    Game.show_help()
    Game.show_top_score()
    a = Game('张三')
    a.start_game()

双下方法

__len__

class B: 
    def __len__(self): 
        return 666 
b = B() 
print(len(b)) # len 一个对象就会触发 __len__方法。

__str__

#如果一个类中定义了str方法,在打印对象时,默认会输出该方法的返回值

class A: 
    def __init__(self): 
        pass 
    def __str__(self): 
        return '123' 
a = A() 
print(a) 
print('%s' % a)

__call__

class Foo: 
    def __init__(self): 
        print('__init__') 
    def __call__(self, *args, **kwargs): 
        print('__call__') 

obj = Foo() # 执行 __init__ 
obj() # 执行 __call__

print(callable(obj))#判断是否可调用,如果删掉call会报false

__eq__

class A: 
    def __init__(self): 
        self.a = 1 
        self.b = 2 
    def __eq__(self,obj): 
        if self.a == obj.a and self.b == obj.b:
         return True 
         
a = A() 
b = A() 
print(a == b)#判断两个值是否相等,如果没有eq会报错

__new__

new方法是类准备将自身实例化时调用的,其始终是类的静态方法,通常来说,新式类开始实例化的时候,new方法会返回cls(当前类)的示例,然后init方法作为构造方法会接收这个实例(self)作为自己的第一个参数,然后依次传入new方法中接收的位置参数和命名参数

单例模式:使得一个类只有一个实例

class A: 
    __instance = None 
    def __new__(cls, *args, **kwargs): 
        if cls.__instance is None: 
            obj = object.__new__(cls) 
            cls.__instance = obj 
            return cls.__instance

__enter__,__exit__,上下文管理器

#自定义文件管理器
class Diycontextor: 
    def __init__(self, name, mode): 
        self.name = name 
        self.mode = mode
        def __enter__(self): #开启上下文管理器对象时触发
            print("Hi enter here!!") 
            self.filehander = open(self.name, self.mode) 
            return self.filehander 
        def __exit__(self,*args): #退出时执行
            print("Hi exit here") 
            self.filehander.close() 
            
with Diycontextor('config', 'r') as f: 
    for i in f: 
        print(i.strip())

Property

我们可以用@peoperty装饰器来创建只读属性,他能把函数当作属性来访问并执行

class Goods(object):
    def __init__(self):
        # 原价
        self.original_price = 100
        # 折扣
        self.discount = 0.8
    @property
    def price(self):
        # 实际价格 = 原价 * 折扣
        new_price = self.original_price * self.discount
        return new_price

    @price.setter
    def price(self, value):
        self.original_price = value

    @price.deleter
    def price(self):
        del self.original_price

obj = Goods()
print(obj.price)  # 获取商品价格
obj.price = 200 # 修改商品原价
print(obj.price)
del obj.price # 删除商品原价

垃圾回收

引用计数器为主,分代回收和标记清除为辅

引用计数器

python中创建的任何对象都会放在refchain的双向列表中,如

list = ['11','22','sda']#列表对象
a = 18#整形对象

这些对象都会放到双向列表中,也就是说得到了refchain,也就得到了python中所有的对象

当创建一个对象的时候,对象的内部都会有这样的一些属性:

  1. 指向上一个对象的指针
  2. 指向下一个对象的指针
  3. 类型
  4. 引用的个数
#define PyObject_HEAD        PyObject ob_base;
#define PyObject_VAR_HEAD        PyVarObject ob_base;

// 宏定义,包含 上一个、下一个,用于构造双向链表用。(放到refchain链表中时要用到)
#define _PyObject_HEAD_EXTRA            \
    struct _object *_ob_next;            \
    struct _object *_ob_prev;

typedef struct _object {
    _PyObject_HEAD_EXTRA;    // 用于构造双向链表
    Py_ssize_t ob_refcnt;    // 引用计数器
    struct _typeobject *ob_type;    // 数据类型
} PyObject;

typedef struct {
    PyObject ob_base;        // PyObject对象
    Py_ssize_t ob_size;        // Number of items in variable part,即:元素个数
} PyVarObject;

源码中有两个重要的struct:PyObject,PyVarObject

PyObject:指向上/下一个对象的指针,类型,引用计数器

PyVarObject:存储由多个元素组成的类型的值,像字符串

何为引用个数:

当我们创建一个对象,如a = 'using',会给using开辟一个内存空间存放到双向列表里,如果我们这时b = a ,会把b指向using,而不是重新弄一个新的对象,这时using的引用个数变成了2

何时计数增加

  1. a = 2对象被创建
  2. b = a对象被引用
  3. func(a)对象被作为参数传到函数中
  4. list = [1,2,'s',a]对象被作为元素存储在容器中

何时计数减少:

  1. 当该对象的别名被显示销毁时 del a
  2. 当对象的别名被赋予新值 a = 26
  3. 当对象离开作用域,如当func函数执行完毕,函数里面的局部变量引用计数器就会减一(全局变量不会)
  4. 当元素从容器中删除或者容器被销毁

当计数为0,将进行垃圾回收

循环引用的问题

a1 = [1,2,3]
b1 = [2,3,4]
a1.append(b1)#b1对应的[2,3,4]的对象引用为2
b1.append(a1)#a1对应的对象引用为2
del a1#引用计数器减一
del b1
#a1,b1计数器都为1

这样虽然a1,b1被我们销毁了,但是对应的对象依旧存在内存中没有被回收

评价

优点:简单,没有引用就会被释放,实时性

缺点:循环引用,维护计数消耗的资源

标记清除

原理

再去维护一个链表,这个链表专门用于存放那些可以循环引用的对象,python会在一些情况下去扫描这个链表里面的每个元素,如果检查到循环引用,则让双方的引用计数器都各自减一,如果是0则垃圾回收

标记清除算法

对象之间通过指针连在一起构成一个有向图,从根节点出发,腌着有向边遍历对象,可达的对象标记为活动对象,不可达的就是要被清除的非活动对象。跟对象就是全局变量、调用栈、寄存器。如图,从黑点出发,123可达,那就会标记为活动对象,44不可达,会被回收

avatar

问题:
什么时候去扫描?

标记和清除的过程效率并不高,清除非活动的对象千必须顺序扫描整个堆内存,即扫描所有的对象

分代回收

原理:设置三个列表

0代:0代中对象个数打到700个,扫描一次

1代:0代扫描10次,则1代扫描1次

2代:1代扫描10次,则2代扫描1次

avatar

当0代中个数达到700时,会对0代中所有元素进行一次扫描,如果是循环引用则计数器-1,然后判断是否为0进行回收。如果没有回收,则从0代升级到1代,这时候0代是空,1代会记录0代依旧扫描1次

弱代价说:年轻的对象通常死的快,而老对象可能存活时间更长

缓存机制

缓存在python中分为两大类

python中为了避免重复创建和销毁而维持的一些常见对象,如-5到256,bool,一些字符或字符串,这些都是常用值,会在内存中先把这些值都创建好。可以用id()函数来验证

因为代码块的缓存机制,交互模式和命令模式下测试的结果可能不同。一个模块、函数、类、文件等都是一个代码块。而在交互模式下,一行就是一个代码块

同一个代码块中的缓存机制:

python在执行同一个代码块的初始化对象的命令时,会检查这个是否已经存在,如果是,则引用,如果不是,则开辟新的内存

适用对象:int(float),str,bool

从同一块代码块中因为驻留机制,对于上面那些数据类型,只要对象相同,内存地址就共享,从不同代码块中只有引用对象为池中常见对象,才会有内存共享

free_list

python为了内部优化,当一个对象的引用计数为0时,不会立即回收,而是添加到free_list链表中当作缓存,以后创建对象就不再重新开辟内存,而是使用free_list,但是free_list容量有限,默认数量为80,当满了的时候会去直接销毁,代表性 的有float,list,tuple,dict

异常是程序运行时发生错误的信号

我们可以使用异常处理机制来捕捉异常,当捕捉成功后,将进入另外的一个处理分支,执行预定的逻辑

try:
    a = input('please input a number')
    int(a)
except:
    print('你输入的不是数字!')
finally:
    print('程序结束')#不管对不对,都会执行

也可以打印特定错误

s1 = 'hello' 
try:
    int(s1) 
except ValueError as e: 
    print(e)
print('输入错误')#用捕获机制这行也会运行
#Exception万能异常种类

主动报错

try:
    raise TypeError('类型错误') 
except Exception as e: 
    print(e)

断言

assert 1 == 1 
assert 1 == 2

json

序列化
将原本的字典、列表等内容转化为一个字符串的过程叫做序列化

序列化的目的:

  1. 以某种存储形式使自定义对象持久化
  2. 将对象从一个地方传递到另外一个地方
  3. 使程序更具有维护性
    json模块提供四个功能:dumps\dump\loads\load

dumps:从变量里将字典类型的数据转换为str

loads:将str转换为dict

dump:将字典转换为str并写道json文件里面

load:从json中读取数据

import json
dict = {'e1':4,'da':6,'cds':75}
str_dict = json.dumps(dict)#序列化,将一个字典转化为字符串
print(type(dict),dict)
print(type(str_dict),str_dict)

dic = json.loads(str_dict)
print(type(dic),dic)#反序列化

dict1 = {'你好':1,'谢谢':2,'小笼包':3,'再见':4}

json_dict1 = json.dumps(dict1,sort_keys=True,indent=2,separators= (',',':'),ensure_ascii=True)
print(json_dict1)#ensure_ascii如果不关掉,中文都会以\uxxxx显示
json_dict = json.dumps(dict1,ensure_ascii=False)
print(json_dict)

with open('test.txt','w',encoding='utf-8') as f:
    json.dump(dict1,f,ensure_ascii=False)#写入文件

# <class 'dict'> {'e1': 4, 'da': 6, 'cds': 75}
# <class 'str'> {"e1": 4, "da": 6, "cds": 75}
# <class 'dict'> {'e1': 4, 'da': 6, 'cds': 75}
# {"\u4f60\u597d": 1, "\u8c22\u8c22": 2, "\u5c0f\u7b3c\u5305": 3, "\u518d\u89c1": 4}
# {"你好": 1, "谢谢": 2, "小笼包": 3, "再见": 4}

# test.txt
# {
#   "\u4f60\u597d":1,
#   "\u518d\u89c1":4,
#   "\u5c0f\u7b3c\u5305":3,
#   "\u8c22\u8c22":2
# }
Skipkeys
1,默认值是False,如果dict的keys内的数据不是python的基本类型,2,设置为False时,就会报 TypeError的错误。此时设置成True,则会跳过这类key,3,当它为True的时候,所有非ASCII 码字符显示为\uXXXX序列,只需在dump时将ensure_ascii设置为False即可,此时存入json的 中文即可正常显示。
indent 是一个非负的整型,如果是0就是顶格分行显示,如果为空就是一行最紧凑显示,否则会换行 且按照indent的数值显示前面的空白分行显示,这样打印出来的json数据也叫pretty-printed json 
ensure_ascii 当它为True的时候,所有非ASCII码字符显示为\uXXXX序列,只需在dump时将ensure_ascii设 置为False即可,此时存入json的中文即可正常显示。 
separators 分隔符,实际上是(item_separator, dict_separator)的一个元组,默认的就是(‘,’,’:’);这表示 dictionary内keys之间用“,”隔开,而KEY和value之间用“:”隔开。 
sort_keys 将数据根据keys的值进行排序

hashlib

hashlib提供了常见的摘要算法,如MD5,SHA1

import hashlib

md5 = hashlib.md5()#sha1调用方式相同
md5.update('hello world'.encode('utf-8'))
print(md5.hexdigest())

configparser

用于生成与windows.ini风格类似的配置文件,包含一个或多个节,每个节包含多个键值对

import configparser
conf = configparser.ConfigParser()

conf['DEFAULT'] = {'ServerAliveInterval':'45',
                   'Compression':'yes',
                   'CompressionLevel':'9',
                   'ForwardX11':'yes' }
conf['xxx.org'] = {'User':'hg'}
conf['shangxizhuan.site'] = {'Port':'50022',
                                'ForwardX11':'no' }
with open('config','w') as config:
    conf.write(config)
#查找并打印
print(conf['shangxizhuan.site']['Port'])
for key in conf['xxx.org']:
    print(key)

增删改查

import configparser
conf = configparser.ConfigParser()

conf.read('config')

conf.add_section('local')#添加键

conf.remove_section('xxx.org')#删除节

conf.remove_option('shangxizhuan.site','Port')#移除条目

conf.set('shangxizhuan.site','port','11111')#再对于键吓加上条目

conf.write(open('config.new','w'))#写入文件

logging

logging模块默认状态吓将日志打印到标准输出中,切只显示大于WARNING的日志(日志级别等级CRITICAL > ERROR > WARNING > INFO >DEBUG),默认格式为日志级别:Logger名称:用户输出的消息

logging库提供了多个组件:logger\handler\formatter,logger对象提供课直接使用的接口,handler发送日志到合适的目的地,filter提供过滤日志信息的方法,formatter指定日志显示格式。logger.setLevel()\fh.setLevel()都可以对文件流进行级别设置

import logging

logging.basicConfig(level=logging.INFO,
                    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
                    datefmt='%a, %d %b %Y %H:%M:%S',
                    # filename='test.log',
                    # filemode='w')
                    )
logger = logging.getLogger(__name__)#初始化,name可以不填

logger.info("Start print log")
logger.debug("Do something")
logger.warning("Something maybe fail.")
logger.info("Finish")

'''
logging.basicConfig()函数通过具体参数更改logging模块默认行为,参数具体为:
filename\filemode:创建日志文件,指定文件打开方式,如果不写则日志信息会被输出到控制台
format:指定handler使用的日志现实格式
datefmt:指定日期时间格式
level:设置从哪一级别开始报

format的参数:
参数:作用
 
%(levelno)s:打印日志级别的数值
%(levelname)s:打印日志级别的名称
%(pathname)s:打印当前执行程序的路径,其实就是sys.argv[0]
%(filename)s:打印当前执行程序名
%(funcName)s:打印日志的当前函数
%(lineno)d:打印日志的当前行号
%(asctime)s:打印日志的时间
%(thread)d:打印线程ID
%(threadName)s:打印线程名称
%(process)d:打印进程ID
%(message)s:打印日志信息
'''

对文件操作


import logging

logger = logging.getLogger()#创建一个handler,用于写入日志文件
logger.setLevel(level=logging.INFO)
handler = logging.FileHandler("log.txt")#用于写入文件

ch = logging.StreamHandler()#用于输出到控制台
handler.setLevel(logging.INFO)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter)
ch.setFormatter(formatter)

logger.addHandler(handler)# looger对象可以添加多个handler对象
logger.addHandler(ch)

logger.info("Start print log")
logger.debug("Do something")
logger.warning("Something maybe fail.")
logger.info("Finish")

logging的配置文件

import os
import logging.config
import datetime
import time

# 定义三种日志输出格式 开始

standard_format = '[%(asctime)s][%(threadName)s:%(thread)d][task_id:%(name)s][%(filename)s:%(lineno)d]' \
                  '[%(levelname)s][%(message)s]'  # 其中name为getlogger指定的名字

simple_format = '[%(levelname)s][%(asctime)s][%(filename)s:%(lineno)d]%(message)s'

id_simple_format = '[%(levelname)s][%(asctime)s] %(message)s'

# 定义日志输出格式 结束

logfile_dir = os.path.dirname(os.path.abspath(__file__))  # log文件的目录

date_time = str(datetime.date.fromtimestamp(time.time()))

logfile_name = 'access' + date_time + '.log'  # log文件名

# 如果不存在定义的日志目录就创建一个
if not os.path.isdir(logfile_dir):
    os.mkdir(logfile_dir)

# log文件的全路径
logfile_path = os.path.join(logfile_dir, logfile_name)

# log配置字典
LOGGING_DIC = {
    'version': 1,
    'disable_existing_loggers': False,
    'formatters': {
        'standard': {
            'format': standard_format
        },
        'simple': {
            'format': simple_format
        },
    },
    'filters': {},
    'handlers': {
        # 打印到终端的日志
        'console': {
            'level': 'DEBUG',
            'class': 'logging.StreamHandler',  # 打印到屏幕
            'formatter': 'simple'
        },
        # 打印到文件的日志,收集info及以上的日志
        'default': {
            'level': 'DEBUG',
            # 'class': 'logging.handlers.RotatingFileHandler',  # 保存到文件
            "class": "logging.handlers.TimedRotatingFileHandler",  # 依照时间进行切割
            'formatter': 'standard',
            'filename': logfile_path,  # 日志文件
            # 'maxBytes': 1024*1024*5,  # 日志大小 5M
            "when": "h",  # 小时格式
            "interval": 24,  # 24 小时
            'backupCount': 7,  # 7个
            'encoding': 'utf-8',  # 日志文件的编码,再也不用担心中文log乱码了
        },
    },
    'loggers': {
        '': {
            'handlers': ['default', 'console'],  # 这里把上面定义的两个handler都加上,即log数据既写入文件又打印到屏幕
            'level': 'DEBUG',
        },
    },
}


def load_my_logging_cfg():
    logging.config.dictConfig(LOGGING_DIC)  # 导入上面定义的logging配置
    logger = logging.getLogger(__name__)
    logger.info('hello!')


if __name__ == '__main__':
    load_my_logging_cfg()

# 我们只需把配置写道字典中就行
# 1.从字典中加载配置logging.config.dictConfig(LOGGING_DIC)
# 2.logger对象来产生日志,logger对象都是配置到字典的loggers键对应的子字典

time

time.sleep(sec):线程推迟一定时间后再运行,单位为秒

time.time():获取当前时间戳

python中一般有三种表示时间的方式:时间戳、元组、格式化的时间字符串

  1. 时间戳:从1970年1月1日00:00:00到现在偏移的秒数,float,time.time()
  2. 元组(结构化时间):struct_time元组共有9个元素共九个元素:(年,月,日,时,分,秒,一年中第几周,一年中第几天等),time.localtime()
  3. 格式化的时间字符串(Format String)‘1999-12-06’,time.strftime()

格式化表示法

%y 两位数的年份表示(00-99)
%Y 四位数的年份表示(000-9999)
%m 月份(01-12)
%d 月内中的一天(0-31)
%H 24小时制小时数(0-23)
%I 12小时制小时数(01-12)
%M 分钟数(00=59)
%S 秒(00-59)
%a 本地简化星期名称
%A 本地完整星期名称
%b 本地简化的月份名称
%B 本地完整的月份名称
%c 本地相应的日期表示和时间表示
%j 年内的一天(001-366)
%p 本地A.M.或P.M.的等价符
%U 一年中的星期数(00-53)星期天为星期的开始
%w 星期(0-6),星期天为星期的开始
%W 一年中的星期数(00-53)星期一为星期的开始
%x 本地相应的日期表示
%X 本地相应的时间表示
%Z 当前时区的名称
%% %号本身

三种时间的转换关系:
avatar

import time
print(time.time())#时间戳
print(time.localtime())#结构化时间,元组
print(time.strftime('%Y-%m-%d %H-%M-%S'))#格式化时间

#时间戳————》结构化
t12 = time.localtime(t1)
print(t12)

#结构化————》时间戳
t21 = time.mktime(t2)
print(t21)

#结构化————》格式化
t23 = time.strftime('%Y-%m-%d %H-%M-%S',t2)
print(t23)

#格式化————》结构化
t32 = time.strptime(t3,'%Y-%m-%d %H-%M-%S')
print(t32)

计算时间差

start_time=time.mktime(time.strptime('2017-09-11 08:30:00','%Y-%m-%d %H:%M:%S'))
end_time=time.mktime(time.strptime('2019-09-12 11:00:50','%Y-%m-%d %H:%M:%S'))
dif_time=end_time-start_time 
struct_time=time.gmtime(dif_time)
print('过去了%d年%d月%d天%d小时%d分钟%d秒'%(struct_time.tm_year- 1970,struct_time.tm_mon-1, struct_time.tm_mday- 1,struct_time.tm_hour, struct_time.tm_min,struct_time.tm_sec))

os

OS模块是与操作系统交互的一个接口

路径

os.chdir("dirname"):改变当前脚本工作目录,等于cd
os.curdir()返回当前目录
os.pardir()获取当前目录的父目录字符串名

文件夹

os.mkdir('dirname')生成单机目录
os.rmdir('dirname')删除单级空目录
os.removedirs('dirname')向上递归删除空目录
os.remove()删除文件
os.rename('old','new')重命名
os.stat('path/file')获取文件目录信息

操作系统差异

os.system("bash command") 运行shell命令,直接显示
os.popen("bash command).read() 运行shell命令,获取执行结果
os.environ 获取系统环境变量

路径相关

os.path.abspath(path) 返回path规范化的绝对路径
os.path.split(path) 将path分割成目录和文件名二元组返回
os.path.dirname(path) 返回path的目录。其实就是os.path.split(path)的第一个元素
os.path.basename(path) 返回path最后的文件名。如何path以/或\结尾,那么就会返回空
值,即os.path.split(path)的第二个元素。
os.path.exists(path) 如果path存在,返回True;如果path不存在,返回False
os.path.isabs(path) 如果path是绝对路径,返回True
os.path.isfile(path) 如果path是一个存在的文件,返回True。否则返回False
os.path.isdir(path) 如果path是一个存在的目录,则返回True。否则返回False
os.path.join(path1[,
path2[, ...]]) 将多个路径组合后返回,第一个绝对路径之前的参数将被忽略
os.path.getatime(path) 返回path所指向的文件或者目录的最后访问时间
os.path.getmtime(path) 返回path所指向的文件或者目录的最后修改时间
os.path.getsize(path) 返回path的大小

re

正则表达式模块
re.findall('a[abc]b', 'aab abb acb adb afb a_b')

\w 匹配字母(包含中文)或数字或下划线
\W 匹配非字母(包含中文)或数字或下划线
\s 匹配任意的空白符
\S 匹配任意非空白符
\d 匹配数字
\D 匹配非数字
\A 从字符串开头匹配
\z 匹配字符串的结束,如果是换行,只匹配到换行前的结果
\n 匹配一个换行符
\t 匹配一个制表符
^ 匹配字符串的开始
$ 匹配字符串的结尾
.
匹配任意字符,除了换行符,当re.DOTALL标记被指定时,则可以匹配包括换行符的任
意字符。
[...] 匹配字符组中的字符
[^...] 匹配除了字符组中的字符的所有字符
* 匹配0个或者多个左边的字符。
+ 匹配一个或者多个左边的字符。
? 匹配0个或者1个左边的字符,非贪婪方式。
{n} 精准匹配n个前面的表达式。
{n,m} 匹配n到m次由前面的正则表达式定义的片段,贪婪方式
a b
() 匹配括号内的表达式,也表示一个组

shutil

文件、文件夹、压缩包处理模块

文件相关

import shutil
#把文件内容拷贝到另外一个文件中
shutil.copyfileobj(open('db.txt','r'),open('log.txt','w'))

#拷贝文件,目标文件无需存在
shutil.copyfile('db.txt','db.txt.bat')

#仅拷贝权限,内容、用户、组都不变,m目标文件必须存在
# shutil.copymode('a','b')

#仅拷贝状态信息,目标文件必须存在
# shutil.copystat('a','b')

#拷贝文件和权限,目标文件必须存在
# shutil.copy('a','b')

#递归拷贝文件夹
# shutil.copytree(src, dst, symlinks=False,ignore=None)

#递归删除
# shutil.rmtree('xx')

#递归移动,可用于重命名
# shutil.move('xx1','xx2')

压缩包相关

ret = shutil.make_archive('db.txt','zip',root_dir='./')
压缩包名,压缩种类,目的路径,用户,组,日志

shutil使用zipfile和tarfile两个模块来处理压缩包

zipfile


import zipfile 
# 压缩 
z = zipfile.ZipFile('laxi.zip', 'w') 
z.write('a.log') 
z.write('data.data') 
z.close() 
# 解压 
z = zipfile.ZipFile('laxi.zip', 'r') 
z.extractall(path='.') 
z.close()

tarfile

import tarfile 
# 压缩文件 
t = tarfile.open('/tmp/egon.tar','w') 
t.add('/test1/a.py',arcname='a.bak') 
t.add('/test1/b.py',arcname='b.bak') 
t.close() 
# 解压缩文件 
t = tarfile.open('/tmp/egon.tar','r') 
t.extractall('/egon') 
t.close()

附加

jieba

分词用

pyinstaller

用于打包程序,在cmd里面使用
pyinstaller -F -i 图标.ico 代码.py

匿名函数

函数名 : lambda 参数 : 返回值

calc = lambda x,y : x*y
print(calc(2,6))

dic = {'k1':20,'k2':120,'k3':50}
print(dic[max(dic,key=lambda k :dic[k])])

常用内置函数

eval:执行字符串类型的代码,并返回最终结果

con = eval('print("hello")')       

exec:执行字符串类型的代码,但无返回

map():接收函数和list,并把每个元素依次作用在函数上,得到一个新的list并返回

list = map(lambda x : x*x,[i for i in range(1,11)])
for i in list:
    print(i)

globals():以字典格式返回全局变量

iter():生成迭代器

next():返回迭代器的下一个项目

len():返回对象长度或者元素个数

open():用于打开文件,创建一个file对象

print():打印输出

input():接收一个标准输入,返回为str

range():用于创建一个整数列表

dir():返回参数的属性、方法列表

dict():将元组、列表转化为字典

chr():ascii转字符

list():元组转字符

abs():返回绝对值

slice():实现切片对象

# 语法:
# class slice(stop)
# class slice(start, stop[, step])

s5 = slice(5)    
arr = [i for i in
print(arr)       
print(arr[s5])   

# [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
# [0, 1, 2, 3, 4]

sorted():将可迭代的对象进行排序(默认升序)操作

zip:将可迭代的对象中的元素打包成一个个元组

s5 = slice(5)                         
arr = [chr(i) for i in range(97,123)] 
no = [i for i in range(1,len(arr)+1)] 
print(arr)                            
print(no)                             
list = list(zip(arr,no))              
print(list)  

# ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z']
# [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26]
# [('a', 1), ('b', 2), ('c', 3), ('d', 4), ('e', 5), ('f', 6), ('g', 7), ('h', 8), ('i', 9), ('j', 10), ('k', 11), ('l', 12), ('m', 13), ('n', 14), ('o', 15), ('p', 16), ('q', 17), ('r', 18), ('s', 19), ('t', 20), ('u', 21), ('v', 22), ('w', 23), ('x', 24), ('y', 25), ('z', 26)]

__import__():用于动态加载类和函数

filter:过滤

def fun(x): return x%2==0
list = filter(fun,[i for i in range(10)])
print(list)
for i in list:
    print(i)

# <filter object at 0x000001F55B3C56A0>
# 0
# 2
# 4
# 6
# 8

max/min:返回最大/最小值。对字典默认按照key