Skip to content

Latest commit

 

History

History
1816 lines (1281 loc) · 44.2 KB

File metadata and controls

1816 lines (1281 loc) · 44.2 KB

InterviewPython 学习

干货

【GitHub】awesome-python

【GitHub】Python初学者

【公众号】Python开发者

【公众号】Python中文社区

【官网】django中文文档

【官网】django-rest-framework

自己总结的

python运行原理

python程序的生命周期

(1)源码阶段(.py文件)

(2)编译阶段(PyCodeObject字节码对象)

(3)运行阶段(Python虚拟机运行字节码指令)

python程序的运行依赖python解释器,执行一个.py文件,首先是将.py文件编译成PyCodeObject字节码对象,并存入内存中。接下来,python虚拟机逐条运行字节码指令。当运行完毕后,会生成.pyc文件,.pyc文件是PyCodeObject字节码对象在硬盘中的表现形式。

当下次再运行相同的.py文件时,假如源码没有任何改动,则不会再次将文件编译成PyCodeObject字节码对象,而是优先将.pyc文件载入到内存中去执行相应的字节码指令。

.pyc文件包含哪些信息?

一个 pyc 文件包含了三部分信息:Python 的 magic number、pyc 文件创建的时间信息,以及 PyCodeObject 对象。

注意:一个.py文件,只有被当作module时,才会生成.pyc文件,也就是假如文件中有 if __name__ == '__main__' :,将不会为这个.py文件生成.pyc文件,除非这个文件同时被其他运行的文件所引用(import

如何查看python的字节码指令

import dis

with open('xxx.py','r') as f:
        # print(f.read())
        co = compile(f.read(),'xxx.py','exec')
        print(dis.dis(co))

数据类型

基本数据类型(8种)

  1. 整型(int)
  2. 浮点型(float)
  3. 字符串(str)
  4. 列表(list)
  5. 元祖(tuple)
  6. 字典(dict)
  7. 集合(set)
  8. 布尔(bool)

数据类型分类

分类 python 数据类型
数值类型 整数、浮点、布尔
序列类型 字符串、列表、元祖
散列类型 字典、集合

字节类型表示a=bytes('123') or a=b'123'

字节数组bytearray('123')

可变序列:列表[],集合{},字典{"key":'value'}

不可变序列:字符串'',元祖()

有序序列:列表[],字符串'',元祖()

无序散列:集合{},字典{"key":'value'}


数据类型方法

字符串方法

  1. 字符串拼接:
    • str1 + str2
    • "".join([str1, str2]) 数组转字符串的常用方法
    • "str1:%s,str2:%s"%(str1, str2)
    • "{}{}{}".format(str1, str2, str3)
  2. str1.replace(m,n,x)
  • 字符串替换
  • m:新字符串,n:旧字符串,x:替换个数
  1. str1.index(m)
    • 查找mstr1位置(索引),找不到会抛出异常
  2. str1.find(m)
    • 查找mstr1位置(索引),找不到返回 -1
  3. str1.count(m)
    • 统计mstr1中出现的次数,一个都没有返回 0
  4. str1.isdigit()
    • 判断str1是否是数字,返回 bool
  5. str1.isalpha()
    • 判断str1是否是字母,返回bool
  6. str1.isupper()
    • 判断str1是否是大写,返回bool
  7. str1.islower()
    • 判断str1是否是小写,返回bool
  8. str1.startswith(m)
    • 判断str1是不是以m开头,返回bool
  9. str1.endswith(m)
    • 判断str1是不是m结尾,返回bool
  10. str1.upper()
    • str1转化为大写
  11. str1.lower()
    • str1转化为小写
  12. str1.strip()
    • 去掉str1左右空白字符串
    • .lstrip()去掉左侧空白
    • .rstrip()去掉右侧空白
  13. str1.title()
    • str1标题化
  14. str1.capitalize()
    • str1第一个字母变成大写
  15. str1.split(m, x)
    • m为界分割,分割x

列表方法

  1. li.append(m)

    • m添加到列表末尾
  2. li.insert(x,m)

    • m插入到列表,x是列表元素下标
  3. li.extend(list1)

    • 列表拼接,li 尾部增加 list1 的元素,作用在 li 上
    • 这个方法也是充分体现了鸭子类型,传入的参数不仅是列表,元组、集合等可迭代对象都可以当作参数传入
    • li+list1区别:li + list1是表达式,要用一个变量来接收,如 list2 = li + list1
  4. li.pop(x)

    • x:被删除元素的索引,返回值:被删除的元素
    • 若不传参数,则从最后开始删除
  5. li.remove(m)

    • 删除一个元素m,没有返回值
  6. li.clear()

    • 清空列表li
  7. li.index(m)

    • 查询m的下标
  8. li.count(m)

    • 统计mli中出现的次数,一个都没有返回 0
  9. li[x] = m

    • li中下标为x的元素的值,设置成m
  10. 深拷贝和浅拷贝

    • copy.copy(li) 浅拷贝,只拷贝第一层元素的引用,产生的新的列表与被拷贝的列表互不影响
    • copy.deepcopy(li)深拷贝,递归拷贝所有元素,产生新的列表与被拷贝的列表互不影响
    • li = old_li 赋值,两个变量都指向同一个内存块,修改li会对old_li产生影响,同理,修改old_li也会对li产生影响
  11. 永久排序

    • li.sort(reverse=True/False)
    • True 倒序
    • False 正序
    • li.reverse()
      • 永久倒序
  12. 临时排序

    • sorted(li, reverse=True/False)
      • True 倒序
      • False 正序
    • reversed(li)
      • 临时倒序
  13. 数组遍历

    lists = [1, 2, 3, 4, 5]
    
    print("--------# 只遍历值------------")
    # 只遍历值
    for i in lists:
        print(i)
    
    print("--------# 逆序遍历--1----------")
    
    # 逆序遍历
    for i in lists[::-1]:
        print(i)
    
    print("--------# 逆序遍历--2----------")
    # 逆序遍历
    for i in range(len(lists), 0, -1):
        print(i)
    
    print("--------# 遍历键和值--2----------")
    # 遍历键和值
    for idx, val in enumerate(lists):
        print(idx,':',val)
    
    
    print("--------# 遍历键----------")
    # 只遍历键
    for idx in range(0, len(lists)):
        print(idx)
        

元组方法

  1. tup.index(m)
    • 查找mtup中下标
  2. tup.count(m)
    • 统计mtup中出现的次数,一个都没有返回 0

集合方法

  1. st1 & st2
    • st1st2 的交集
  2. st1 | st2
    • st1st2 的并集
  3. st1 - st2
    • st1st2 的差集
  4. st.add(m)
  • 向集合st里面添加一个元素m
  1. st.pop()
    • 随机删除集合里的元素
  2. st.remove(m)
    • 指定删除集合里的元素m,若集合没有元素m,也不会报错
  3. st1.isdisjoint(st2)
    • 判断st1st2是否存在交集
  4. st1.issubset(st2)
    • 判断st1是否是st2的子集
  5. st1.issuperset(st2)
    • 判断st1是否是st2的父集
  6. st1.update(m)
    • 向集合里面添加元素mm可以为字符串列表元组集合字典(字典只存key)

字典方法

  1. d = dict.fromkeys(m, v)
    • 生成一个字典d,键是可迭代对象m中的元素,值是默认值v
  2. d.setdefault(k, v)
    • 查询字典d中有没有k这个键,有则返回k对应的值;无则添加,d[k]=v
  3. d.clear()
    • 清空字典
  4. d.pop(k)
    • 删除以k为键的键值对
  5. d.popitem()
    • 删除最后一个键值对
  6. d.update(new_d)
    • 把new_d的键值对合并到d中
  7. d[k]=v
    • 添加或修改键值对
  8. d.get(k, v)
    • 查询d中是否k这个键值对,若有,返回k的值;若没有,返回默认值v

判断类型的方法

  1. type(变量)
  2. isinstance(变量,类型)

运算符

优先级从上至下,由高到低

运算符 说明
**^ ! 指数、按位翻转、非
*/% // 乘、除、取模、整除
+ - 加、减
>><< 左移、右移
==>=<=><!= 是否相等、大于等于、小于等于、大于、小于、不等于
=+=-=*=/=%=**=//= 赋值(其中a+=b,相当于a=a+b,其他的同理)
isis not 判断id是否相同
innot in 判断成员是否存在
andornot 与、或、非

流程控制

判断

if-else

if 条件:
    语句
else:
	  语句

if-elif-else

if 条件1语句
elif 条件2:
    语句
elif 条件3:
    语句
else:
    语句

类似三目运算符

# 如果满足条件,则执行语句A,否则执行语句B,是if-else的简写
语句A if 条件 else 语句B

循环

while

while 条件:
    语句

while-else

while 条件:
    语句
else:
    循环结束后执行的语句

for-in

for i in 可迭代对象:
    语句

跳出循环

# break 跳出整个循环体
for i in [1, 2, 3, 4, 5]:
    # 当i>3时,跳出循环,不再往下迭代
    if i>3:
        break
    print(i)
    
# [输出] : 1, 2, 3


# continue 跳出本次循环
for i in [1, 2, 3, 4, 5]:
    # 当i=3时,跳出本次循环,进入下次迭代
    if i=3:
        continue
    print(i)
    
# [输出] : 1,2,4,5

函数

定义函数

# 无参无返回值
def foo():
    语句

# 有参无返回值
def foo(x, y):
    z = x + y

# 无参有返回值
def foo():
    x = 1
    return x + 1

# 有参有返回值
def foo(x, y):
    return x + y

空函数

def emptyfunc():
    pass

多个返回值

return多个值,其实是返回一个元组

def foo():
    x, y, z=1, 2, 3
    return x,y,z

# 返回一个元组
tup = foo()
# 元组拆包
x1, y1, z1 = foo()

函数的参数

  1. 必选参数
    • def foo(x, y, z):pass
  2. 默认参数
    • def foo(x=1):pass
    • 假如不传x参数的话,x默认是等于1
  3. 可变参数(*
    • 不定长传参
      • 定义:def foo(*args):pass
      • 调用:foo(1,2,3,4,5)
    • 元组和列表的压包
      • 定义:def foo(*args):pass
      • 调用:foo(*(1,2,3,4,5)) or foo(*[1,2,3,4,5])
  4. 关键字参数(**)
    • 定义:def foo(**kwargs):pass,其中kwargs是一个字典
    • 调用:
      • foo(k1=v1, k2=v2) 参数传入键值对
      • foo(**d) 参数传入字典d
  5. 命名关键字参数(*
    • 定义:
      • def foo(a, b, *, k1, k2, k3):pass a, b是普通参数,k1, k2, k3 是命名关键字参数
      • 命名关键字参数需要一个特殊分隔符*,后面的参数被视为命名关键字参数
      • 如果函数定义中已经有了一个可变参数,后面跟着的命名关键字参数就不再需要一个特殊分隔符*
    • 调用
      • foo(a, b, k1=v1, k2=v2, k3=v3)
  6. 参数组合
    • 定义:def fun(parameter,*args,keyparameter,**kwargs):pass
    • 参数定义顺序:必选参数,默认参数,可变参数,命名关键字参数,关键字参数

函数的递归

# 典型案例:斐波那契数列
def fib_recursion(idx):
    if idx <= 2:
        return 1
    else:
        return fib_recursion(idx-1) + fib_recursion(idx-2)

注意:

  1. 必须设置函数终止条件
  2. 实用递归优点是逻辑简单清晰,缺点是过深调用导致栈溢出

函数作用域

  1. 外部不能访问函数内部变量
  2. 函数内部能够访问外部变量
  3. 函数内部不能修改外部变量,强行修改需要加 global 外部变量 = 新值
  4. 函数内部和函数外部变量名可以相同,但需要注意访问优先级
# 例子一,内部访问同名变量,不要在内部变量定义之前去访问
g = 1

def foo():
    print(g)  # 报错 UnboundLocalError: local variable 'g' referenced before assignment
    g = 2
    print(g)  # 2

foo()
print(g)  # 1

# -------------------- #
# 例子二,采用global关键字,会把内部变量g声明成全局变量,从而在函数内部可以改变全局变量
g = 1

def foo():
    global g
    print(g) # 1
    g = 2
    print(g) # 2

foo()
print(g) # 2

函数式编程

高阶函数

map

用法:map(函数名,列表/元组/集合/字符串)

说明:把传入的函数依次作用于每个元素,处理完后返回的是生成器类型,需要用list生成数据

li = [1, 2, 3, 4, 5]
def add1(x):
    return x + 1
add_li = list(map(add1, li)) # [2, 3, 4, 5, 6]

filter

用法:filter(函数名, 列表/元组/集合/字符串)

说明:filter()把传入的函数依次作用于每个元素,然后根据返回值是True还是False决定保留还是丢弃该元素,处理完后返回的是生成器类型,需要用list生成数据

li = [1, 2, 3, 4, 5, 6]
def even_num(n):
    if n%2 == 0:
        return n
      
even_li = list(filter(even_num, li)) # [2,4,6]

reduce()

用法:reduce(函数名,列表/元组/集合/字符串)

说明:reduce()用于对参数序列中元素进行累积。python3 中,reduce已经被从全局名字空间里移除了,它被放置在functools模块里

from functools import reduce
li = [1, 2, 3, 4, 5]
m = reduce(lambda x, y : x+y, li)   # m=15

返回函数

def outer_foo(*args):
    def inner_foo():
        for i in args:
            print(i)
    return inner_foo

f = outer_foo(1, 2, 3, 4, 5)
f()  # 1 2 3 4 5

函数可以被当作返回值返回

函数的闭包

# 典型案例1: 
# 外部函数只是返回了函数名的列表,但并没有调用。等到内部函数被调用的时候,外部函数已经迭代完毕,最终的i变成3,所以f1()、f2()、f3()相当于执行了三次f(),返回3*3=9
def count():
    fs = []
    for i in range(1, 4):
        def f():
             return i*i
        fs.append(f)
    return fs

f1, f2, f3 = count()
print(f1())   # 9
print(f2())   # 9
print(f3())   # 9

# 典型案例2:
# 外部函数返回的是内部函数名的调用,所以结果和案例1不相同
def count():
    def f(j):
        def g():
            return j*j
        return g
    fs = []
    for i in range(1, 4):
        fs.append(f(i)) # f(i)立刻被执行,因此i的当前值被传入f()
    return fs
f1,f2,f3=count()
print(f1())  # 1
print(f2())  # 4
print(f3())  # 9

匿名函数

语法:lambda 形参:含形参的表达式

f = lambda x:x+1
print(f(1)) # 2

lambda 返回的是函数名(函数地址)

lambda 常常与map、filter、reduce、sorted等联合使用

装饰器

作用:增强函数的功能,其实装饰器可以装饰函数,也可以装饰类

定义装饰器

def decorator(func):
	def wrapper(*args,**kargs): # 可以自定义传入的参数
		print(func.__name__)
		return func(*args,**kargs) # 返回传入的方法名参数的调用
	return wrapper  # 返回内层函数函数名

实用装饰器

# 使用方法1
f = decorator(函数名)  # 装饰器不传入参数时
f = (decorator(参数))(函数名)  # 装饰器传入参数时
f()  # 执行被装饰过的函数

# 使用方法2
@decorator # 已定义的装饰器
def f():  # 自定义函数
    pass
f() # 执行被装饰过的函数

自身不传入参数的装饰器

def login(func):
    def wrapper(*args,**kargs):
        print('函数名:%s'% func.__name__)
        return func(*args,**kargs)
    return wrapper
    
@login
def f():
    print('inside decorator!')

f()

# 输出:
# 函数名:f
# 函数本身:inside decorator!

自身传入参数的装饰器

def login(text):
    def decorator(func):
        def wrapper(*args,**kargs):
            print('%s----%s'%(text, func.__name__))
            return func(*args,**kargs)
        return wrapper
    return decorator

@login('this is a parameter of decorator')  # 等价于 ==> (login(text))(f) ==> 返回 wrapper
def f():
    print('2019-06-13')

f() # 等价于 ==> (login(text))(f)() ==> 调用 wrapper() 并返回 f()

# 输出:
# this is a parameter of decorator----f
# 2019-06-13

内置装饰器

@property :把类内方法当成属性来使用,必须要有返回值,相当于getter;假如没有定义@func.setter修饰的方法的话,就是只读属性。

# property 例子
class Car:

    def __init__(self, name, price):
        self._name = name
        self._price = price

    @property
    def car_name(self):
        return self._name

    # car_name可以读写的属性
    @car_name.setter
    def car_name(self, value):
        self._name = value

    # car_price是只读属性
    @property
    def car_price(self):
        return str(self._price) + '万'


benz = Car('benz', 30)
print(benz.car_name)   # benz
benz.car_name = "baojun"
print(benz.car_name)   # baojun
print(benz.car_price)  # 30万

@staticmethod :静态方法,不需要表示自身对象的self和自身类的cls参数,就跟使用函数一样。

@classmethod :类方法,不需要self参数,但第一个参数需要是表示自身类的cls参数。

class Demo(object):
    text = "三种方法的比较"

    def instance_method(self):
        print("调用实例方法")


    @classmethod
    def class_method(cls):
        print("调用类方法")
        print("在类方法中 访问类属性 text: {}".format(cls.text))
        print("在类方法中 调用实例方法 instance_method: {}".format(cls().instance_method()))

    @staticmethod
    def static_method():
        print("调用静态方法")
        print("在静态方法中 访问类属性 text: {}".format(Demo.text))
        print("在静态方法中 调用实例方法 instance_method: {}".format(Demo().instance_method()))


if __name__ == "__main__":
    # 实例化对象
    d = Demo()

    # 对象可以访问 实例方法、类方法、静态方法
    # 通过对象访问text属性
    print(d.text)
    # 通过对象调用实例方法
    d.instance_method()
    # 通过对象调用类方法
    d.class_method()
    # 通过对象调用静态方法
    d.static_method()

    # 类可以访问类方法、静态方法
    # 通过类访问text属性
    print(Demo.text)
    # 通过类调用类方法
    Demo.class_method()
    # 通过类调用静态方法
    Demo.static_method()

@staticmethod@classmethod的区别和使用场景:

在上述例子中,我们可以看出,

定义静态类方法和类方法时,@staticmethod装饰的静态方法里面,想要访问类属性或调用实例方法,必须需要把类名写上;而@classmethod装饰的类方法里面,会传一个cls参数,代表本类,这样就能够避免手写类名的硬编码。

调用静态方法和类方法时,实际上写法都差不多,一般都是通过类名.静态方法()类名.类方法()。也可以用实例化对象去调用静态方法和类方法,但为了和实例方法区分,最好还是用类去调用静态方法和类方法。

所以,在定义类的时候,假如不需要用到与类相关的属性或方法时,就用静态方法@staticmethod假如需要用到与类相关的属性或方法,然后又想表明这个方法是整个类通用的,而不是对象特异的,就可以使用类方法@classmethod

内置函数

常用函数

len()求长度

min()求最小值

max()求最大值

sorted()排序

sum()求和

dir()查看对象所有属性和方法

type()查看对象属于哪个类

进制转换函数

bin()转换为二进制

oct()转换为八进制

hex()转换为十六进制

ord()字符转ASCII码

chr()ASCII码转字符

高级内置函数

enumerate() 转化为元组标号,通常用于遍历输出列表元素下标

# enumerate 的例子
li = [1, 2, 3, 4, 5]
for idx, ele in enumerate(li):
    print(idx, ':', ele)

eval(str)只能运行一行字符串代码,也可以用于字符串转字典

# eval 的例子
d = eval("{'a':1,'b':2}")
print(type(d))
print(d)

exec(str)执行字符串编译过的字符串,可以运行多行字符串代码

filter(函数名,可迭代对象)过滤器

map(函数名,可迭代对象)对每个可迭代对象的每个元素处理

zip(可迭代对象,可迭代对象)将对象逐一配对

# zip 的例子
# 压缩
l1 = ['a', 'b', 'c', 'd', 'e']
l2 = [1, 2, 3, 4, 5]
l = list(zip(l1,l2)) # [('a', 1), ('b', 2), ('c', 3), ('d', 4), ('e', 5)]
d = dict(zip(l1,l2)) # {'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5}

# 解压
tup = (('a',1),('b',2),('c',3),('d',4))
l1,l2 = zip(*tup)
# l1 ('a', 'b', 'c', 'd')
# l2 (1, 2, 3, 4)

高级特性

切片

切片适用于字符串''、列表[]和元组()

li = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
print(li[0]) # 取下标为0的元素,1
print(li[::]) # 取全部元素,[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
print(li[::2]) # 步长为2取值,[1, 3, 5, 7, 9]
print(li[1:3]) # 左开右闭,[2, 3]
print(li[1:]) # 下标大于等于1的元素,[2, 3, 4, 5, 6, 7, 8, 9, 10]
print(li[:5]) # 下标小于5的元素,[1, 2, 3, 4, 5]
print(li[::-1]) # 列表逆序,[10, 9, 8, 7, 6, 5, 4, 3, 2, 1]

迭代

可迭代对象:可以直接作用于for循环的对象统称为可迭代对象(Iterable)

判断对象是否可迭代

from collections import Iterable


print(isinstanc(对象Iterable))
# True为可迭代
# False为不可迭代

可迭代对象:字符串,列表,元组,字典,集合

遍历可迭代对象(字典除外):

for i in 可迭代对象:
    语句

对字典遍历

# d 是一个字典

# 遍历 keys
for i in d:
    语句
# 或者
for i in d.keys():
    语句
    
# 遍历 values
for i in d.values():
    语句

# 同时遍历 key 和 value
for k,v in d.items():
    语句
    

生成式

列表生成式[返回的参数 for循环 条件判断]

li = [i for i in range(11) if i%2==0]
print(li) # [0, 2, 4, 6, 8, 10]

集合生成式{返回的参数 for循环 条件判断}

s = {i for i in range(1,10) if i%2==0}
print(s)  # {8, 2, 4, 6}

字典生成式{key:value for循环 条件判断}

li=['a','b','c','d','e','f','g','h']
d={i:j for i,j in enumerate(li) if i%2==0}
print(d)  # {0: 'a', 2: 'c', 4: 'e', 6: 'g'}

生成器(返回的参数 for循环 条件判断)

gen = [i for i in range(11) if i%2==0]
print(gen) # [0, 2, 4, 6, 8, 10]

生成器

生成器:为了节省内存空间,提高程序速度,这种一边循环一边计算的机制,称为生成器(Generator)。

next()取值

li=[1, 2, 3, 4, 5]
g=(x for x in li)
print(g)
print(next(g))
print(next(g))
print(next(g))
print(next(g))
print(next(g))

# [输出]:
# <generator object <genexpr> at 0x0000018F628397C8>
# 1
# 2
# 3
# 4
# 5

for循环遍历取值

li=[1, 2, 3, 4, 5]
g=(x for x in li)
print(g)
for i in g:
    print(i)
    
# [输出]:
# <generator object <genexpr> at 0x0000018F628397C8>
# 1
# 2
# 3
# 4
# 5

生成器函数

用yield返回的函数,都可以看作是生成器

def fun():
    yield 1
    yield 2
    yield 3
      
print(next(f))   # 1
print(next(f))   # 2
print(next(f))   # 3
print(next(f))   # 抛出异常:StopIteration

用for循环遍历生成器

f = fun()
for i in f:
  printi# [输出]:
# 1
# 2
# 3

迭代器

迭代器:可以被next()函数调用并不断返回下一个值的对象称为迭代器(Iterator)

实用iter(可迭代对象)可以把可迭代对象转化为迭代器

it = iter([1, 2, 3, 4, 5])
next(it) # 1
next(it) # 2
next(it) # 3

注意:

  1. 凡是可作用于for循环的对象都是Iterable类型
  2. 凡是可作用于next()函数的对象都是Iterator类型,它们表示一个惰性计算的序列
  3. 集合数据类型如list、dict、str等是Iterable但不是Iterator,不过可以通过iter()函数获得一个Iterator对象
  4. Python的for循环本质上就是通过不断调用next()函数实现的

类的定义

class Teacher:
    pass

控制对象的生成过程__new__

__new__是用来控制对象的生成过程的,在对象生成之间调用,如果__new__方法不返回对象,则不会调用__init__

class User:
    def __new__(cls, *args, **kwargs):
        print("in new")
        return super().__new__(cls)
    def __init__(self, name):
        print("in init")
        self.name = name

初始化实例属性__init__

__init__是用来完善对象的,在对象生成之后调用

class Teacher:
  
  def __init__(self, name, age, subject):
      self.name = name
      self.age = age
      self.subject = subject

实例化对象

t = Teacher('Dzreal', 25, 'PE')

实例属性和类属性

class Teacher:
    
    age = 20
    
    def __init__(self, name, age, subject):
          self.name = name
          self.age = age
          self.subject = subject
          
t = Teacher('Dzreal', 25, 'PE')
print(t.age)    # 25
del t.age       # 删除实例属性后,自动调用类属性
print(t.age)    # 20

注意:

  1. 实例属性的优先级高于类属性
  2. 类没有实例属性时会调用类属性

访问限定

伪私有属性 _attrname:可以访问,但不要随意访问

私有属性 __attrname:一般不可访问,强行访问的话,可以通过 实例._类名__attrname来访问

class Teacher:

    def __init__(self, name, age, subject):
        self.__name = name
        self.age = age
        self._subject = subject

    def get_fake_private(self):
        print(self._subject)

    def get_private(self):
        print(self.__name)


t = Teacher('Dzreal', 25, 'PE')

# 通过实例方法访问私有属性
t.get_fake_private()  # PE
t.get_private()  # Dzreal

# 直接访问私有属性
print(t._subject)  # PE
print(t._Teacher__name) # Dzreal
print(t.__name)    # 报错 AttributeError: 'Teacher' object has no attribute '__name'

定制属性访问

hasattr(m,'n') m:实例 n:类里面的属性 确定属性是否存在

getattr(m,'n') m:实例 n:类里面的属性 得到指定类属性的值

setattr(m,'n',x) m:实例 n:类里面的属性 x:设置属性的值 有则改无则添加

delattr(m,'n') m:实例 n:类里面的属性 删除一个类属性

继承

单继承
class A:
    def foo(self):
        print("father foo")

class B(A):
    pass

b = B()
b.foo()  # father foo
多继承

python的继承采用 C3算法+DFS算法(深度优先算法)实现

# 案例一、普通多继承
class A:
    def foo(self):
        print(A)

class B:
    def foo(self):
        print(B)

class C(A):
    pass

class D(B):
    pass

# 继承顺序似乎和C、D的顺序有关,要是改成 class E(D, C), 继承顺序又会不一样
class E(C, D):
    pass

e = E()
e.foo() 
# A

# mro() 或 __mro__ 可以查看继承顺序
print(E.mro()) 
# [<class '__main__.E'>, <class '__main__.C'>, <class '__main__.A'>, <class '__main__.D'>, <class '__main__.B'>, <class 'object'>]
# 即继承顺序是:E->C->A->D->B

# 案例二、菱形结构继承
class A:
    pass

class B(A):
    pass

class C(A):
    pass

class D(C,B):
    pass

print(D.mro())
# [<class '__main__.D'>, <class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>]
# 即继承顺序是:D->C->B->A
# 说明python的继承不完全是深度优先算法实现,而是采用 C3算法+DFS算法(深度优先)
super
class A:
    def __init__(self):
        print("A")

class B(A):
    def __init__(self):
        print("B")
        super().__init__()
        
b = B()

# [输出]:
# B
# A

多态

class Animal:
    def run(self):
        raise AttributeError('子类必须实现这个方法')
 
class People(Animal):
    def run(self):
        print('人正在走')
 
class Pig(Animal):
    def run(self):
        print('猪正在走')

peo=People()
pig=Pig()

peo.run()  # 人正在走
pig.run()  # 猪正在走

注意:

  1. super().__init__()不是调用父类的构造函数,而是调用mro下一个类的构造函数
  2. 假如父类方法foo被子类重写,super().foo()可以调用父类方法
  3. 假如父类方法foo没有被子类重写,self.foo()可以调用父类方法
鸭子类型

python的内置协议(魔法函数)就是一个很典型的鸭子类型的例子:

如果一个对象实现了__getitem__方法,那python的解释器就会把它当做一个collection,就可以在这个对象上使用切片,获取子项等方法。

如果一个对象实现了__iter__next方法,python就会认为它是一个iterator,就可以在这个对象上通过循环来获取各个子项。

# 鸭子类型案例: 
# 下面的例子,之所以能实现鸭子类型,是因为python的变量可以指向任意类型。而在java中,是无法实现的,java中需要指明类型或父类类型,才能实现多态。
class Pig:
    def say(self):
        print("i am a pig")

class Dog:
    def say(self):
        print("i am a dog")
        
class Duck:
    def say(self):
        print("i am a duck")

animal_list = [Pig, Dog, Duck]
for animal in animal_list:
    animal().say()

# [输出]:
# i am a pig
# i am a dog
# i am a duck

魔法函数

python的魔法函数定义之后,不要去调用它,python解释器自己会去调用,定义魔法函数的作用是在于利用python的鸭子类型的特性,给类添加一些特性。

字符串表示

__repr__:在命令行交互模式下会用到,某个类中重写了__repr__方法,就不用调用print函数,只需要输入类名,在控制台下就会调用__repr__,输出__repr__定义的内容

__str__: 调用print(类名),实际上会调用类的__str__方法

集合、序列相关

__len__:获取元素个数

__getitem__

__setitem__

__delitem__

__contains__

迭代相关

__iter__

__next__

可调用

__call__

with上下文管理器

__enter__:开始的时候调用

__exit__:结束的时候调用

数值转换

__abs__

__bool__

__int__

__float__

__hash__

__index__

元类相关

__new__

__init__

属性相关

__getattr____setattr__

__getattribute____setattribute__

__dir__

属性描述符

__get__

__set__

__delete__

协程

__await__

__aiter__

__anext__

__aenter__

__aexit__


IO编程

文件操作

打开文件

# 打开文件
# 模式:
# r 读
# w 写
# a 追加写
# r+ 读写(先读后写)
# w+ 写读(先写后读)
# a+ 追加写读(先写后读)
fp = open(文件路径模式encoding='utf-8')

# 关闭文件
fp.close() 

# 上下文管理器打开文件(会自动close文件)
with open(文件路径模式encoding='utf-8') as fp:
    fp.read()

fp.seek(m):移动文件指针,当前位置向后移动m个字节

fp.tell(m):查看文件指针

fp.flush(m):刷新缓冲区和fp.close()类似

文件读取

fp.read(m):文件全文读取成一个大字符串,m:代表读取m个字节。

fp.readline():按行读取文件,默认只返回第一行,查看全文用while 循环遍历文件

with open('test.txt','r',encoding='utf-8') as fp:
    line = fp.readline()
    while line:
        line = fp.readline()
        print(line)

fp.readlines():把文件按行存成大数组,一般用for循环遍历每行

文件写入

fp.write(内容) 文件中写入内容

fp.writelines(list) 文件中写入列表类型内容

fp.truncate() 文件内容清空

Json操作

需要用到 json 库(import json

json.loads():把 json 格式的字符串转换成 python 对象

json.dumps():把 python 对象转换成 json 格式的字符串

json.load(open(文件路径)):读取文件中 json 形式的字符串元素,转换成python对象

json.dump(open(文件路径)):把 python 对象转换成 json 格式的字符串并存入文件中

jsonpath

Xpath JSONPath 描述
/ $ 跟节点
. @ 现行节点
/ . or [] 取子节点
.. .. 就是不管位置,选择所有符合条件的条件
* * 匹配所有元素节点
[] [] 迭代器标示(可以在里面做简单的迭代操作,如数组下标,根据内容选值等)
&#124 [,] 支持迭代器中做多选
[] ?() 支持过滤操作
n/a () 支持表达式计算
() n/a 分组,JsonPath不支持
import json
import jsonpath


json_str = '[{"id":1,"category":{"id":1,"category":"常用改图工具","sn":"cierlezk","weight":60,"cat_is_show":true,"add_time":"2019-07-13T11:41:00","webconfig":1},"name":"在线修改图片尺寸","desc":null,"url":"https://www.gaitubao.com/","logo":null,"ws_is_show":true,"add_time":"2019-07-13T11:42:00"},{"id":2,"category":{"id":1,"category":"常用改图工具","sn":"cierlezk","weight":60,"cat_is_show":true,"add_time":"2019-07-13T11:41:00","webconfig":1},"name":"图片加水印","desc":null,"url":"https://www.gaitubao.com/shuiyin/","logo":null,"ws_is_show":true,"add_time":"2019-07-13T11:46:00"},{"id":3,"category":{"id":1,"category":"常用改图工具","sn":"cierlezk","weight":60,"cat_is_show":true,"add_time":"2019-07-13T11:41:00","webconfig":1},"name":"在线压缩图片","desc":null,"url":"https://img.top/","logo":null,"ws_is_show":true,"add_time":"2019-07-13T11:46:00"}]'
# json 字符串转成 python 对象
py_obj = json.loads(json_str)
name = jsonpath.jsonpath(py_obj, '$..name')
url = jsonpath.jsonpath(py_obj, '$..url')
print(name)  # ['在线修改图片尺寸', '图片加水印', '在线压缩图片']
print(url)  # ['https://www.gaitubao.com/', 'https://www.gaitubao.com/shuiyin/', 'https://img.top/']

网络编程

http请求

发送http请求用requests库(import requests

发送请求

r = requests.get(url='网址url', params={"k":"v"}) :发送 get 请求

r = requests.post(url='网址url', data={"k":"v"}) :发送 post 请求

r = requests.post(url='网址url', data={"k":"v"}) :发送 put 请求

r = requests.post(url='网址url', data={"k":"v"}) :发送 patch 请求

r = requests.post(url='网址url', **kwargs) :发送 delete 请求

r = requests.head(url='网址url') :发送 head 请求

响应处理

import requests


url = 'http://www.baidu.com'
r = requests.get(url)

print(r.text)  # 获取网页html源码
print(r.json())  # 若返回值是json,把json转成python对象
print(r.status_code) # 获取http响应码
print(dict(r.cookies))  # 获取cookies
print(dict(r.headers))  # 获取headers

try:
    r = requests.get(url)
    r.raise_for_status()
    print(r.text)
except:
    print("http请求出现异常")
socket编程

服务端要做的工作:

  1. 创建socket
  2. 绑定端口
  3. 监听端口
  4. 接收消息
  5. 发送消息

客户端要做的工作:

  1. 创建socket
  2. 连接socket
  3. 发送消息
  4. 接收消息

客户端代码

# 客户端
import socket


if __name__ == '__main__':
    client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    client.connect(('127.0.0.1', 8000))
    while True:
        re_data = input()
        client.send(re_data.encode("utf8"))
        data = client.recv(1024)
        print(data.decode("utf8"))

服务端代码

# 服务端
import socket
import threading


# 处理客户端的请求
def handle_sock(sock, addr):
    while True:
        data = sock.recv(1024)
        print(data.decode("utf8"))
        re_data = input()
        sock.send(re_data.encode("utf8"))
        
if __name__ == '__main__':
    server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server.bind(('0.0.0.0', 8000))
    server.listen()

    #获取从客户端发送的数据
    #一次获取1k的数据
    while True:
        sock, addr = server.accept()

        #用线程去处理新接收的连接(用户)
        client_thread = threading.Thread(target=handle_sock, args=(sock, addr))
        client_thread.start()

多线程、多进程、线程池和进程池编程

多线程

多线程是操作系统能够调度的最小单元

需要用到threading库(import threading

实例化 对象=Thread(target=函数名,args=(函数参数,))

设置线程名 对象.setName(‘线程名’)

获取线程名 对象.getName()

获取当前线程名 threading.current_thread().name

开启线程 对象.start()

线程守护 对象.setDaemon(True) 主线程结束它的子线程就结束

线程阻塞 对象.join() 当子线程结束后主线程才结束

线程锁 解决cpu资源竞争问题

实例化线程锁 对象=threading.Lock()

上锁 对象.acquire()

解锁 对象.release()

可重入锁 对象=threading.RLock() 在同一个线程(函数)里面,锁里面还可以加锁,只要锁acquire的次数和release的次数一样就行

线程间通讯

  1. 共享变量(不是线程安全,需要加锁)
  2. Queue(from queue import Queue,Queue是线程安全的)

线程同步

Q:为什么要线程同步?

A:保证同一时刻,只有加锁的代码段处于执行状态,不会因为cpu资源竞争,不同代码段同时操作某个数据,导致数据错乱,这样保证了线程安全

Q:为什么会出现死锁?

A:(1)锁没有释放,然后又申请了加锁。(2)互相等待,资源互相竞争

Q:怎么解决死锁?

A:(1)申请锁要记得释放锁(2)用可重入锁RLock

注意

  1. 用锁会影响性能
  2. 锁有可能会引起死锁

线程间同步的方式:

  1. Lock

  2. RLock 可重入锁

  3. condition 条件变量,用于复杂的线程间同步。

    • 需要注意线程启动顺序

    • 可以用with语句获得condition

    • 重要方法:condition.notify()condition.wait(),切换conditon时用到

  4. Semaphore 信号量,用于控制进入数量的锁(控制线程并发数量),semaphore.acquire()减少一把锁,semaphore.release()加回来一把锁,锁的数量等于0,就等待

基于函数创建线程
import threading


def mytask(name):
    print("{} task start".format(name))
    time.sleep(2)
    print("{} task stop".format(name))
  
if __name__ == '__main__':
    thread1 = threading.Thread(target="mytask", args=('task1',))
    thread2 = threading.Thread(target="mytask", args=('task2',))
    thread1.start()
    thread2.start()
    thread1.join()
    thread2.join()
    print("线程全部执行完毕")
基于类创建线程
class MyTask(threading.Thread):
    
    def __init__(self, name):
        super().__init__(name=name)
  
    # 必须要重载run方法
    def run(self):
        print("{} task start".format(name))
        time.sleep(2)
        print("{} task stop".format(name))
    
if __name__ == '__main__':
    thread1 = MyTask(task1)
    thread2 = MyTask(task2)
    thread1.start()
    thread2.start()
    thread1.setDaemon(True)  # 主线程结束了,子线程也会结束
    thread2.setDaemon()
    print("线程全部执行完毕")

线程池和进程池

使用concurrent.futures库(from concurrent.futures import ThreadPoolExecutor,

ThreadPoolExecutor(max_workers)线程池

import time
from concurrent.futures import ThreadPoolExecutor, as_completedwait

def mytask(name):
    print("{} task start".format(name))
    time.sleep(2)
    print("{} task stop".format(name))
    return time.time()

if __name__ == '__main__':
    all_tasks = []
    executor = ThreadPoolExecutor(max_workers=3)
    task1 = executor.submit(mytask, ('task1'))
    task2 = executor.submit(mytask, ('task2'))
    all_tasks.append(task1)
    all_tasks.append(task2)
    
    # 判断线程有没有执行完成,done()方法是非阻塞的方法
    print(task1.done()) 
    
    # 可以获取线程执行结果,result()方法是阻塞的方法
    print(task1.result())
    
    # 取消执行线程,只能在线程执行前调用,线程开始或线程结束,无法调用 cancel()
    task2.result()  # false
    
    # as_completed 是生成器,返回已经完成的future对象
    for future in as_completed(all_tasks):
        data = future.result()
        print("completed time is {}".format(data))
        
    # 用 executor.map(函数,iterable)更加能精简代码,map返回的是future.result()
    name_list = ['task3', 'task4', 'task5']
    for data in executor.map(mytask, name_list):
        print(data)
        
    # wait(future序列),等待future序列都运行完了,才往下执行
    future_list = [executor.submit(mytask, (name)) for name in name_list]
    wait(future_list)
    print("线程全部执行完毕")

ProcessPoolExecutor(max_workers)进程池

进程池用法和线程池差不多

多进程

多进程和多线程的使用场景

  • 耗CPU的操作,用多进程
  • 耗IO的操作,用多线程

多进程用multiprocessing模块

from multiprocessing import Process, Pool

def fib(n):
    if n < 2:
        return 1
    return fib(n-1) + fib(n-2)

if __name__ == '__main__':
    # 进程
    process = Process(target=fib, args=(10,))
    process.start()
    process.join()
    
    # 进程池 multiprocessing的进程池
    pool = multiprocessing.Pool(multiprocessing.cpu_count())
    # 异步启动进程池,返回值是类似future的对象
    result = pool.apply_async()
    # 不再接收新的任务(在pool.join()之前,一定要有pool.close())
    pool.close()
    # 等待所有进程执行完
    pool.join()
    # 返回进程执行返回值
    result.get()

进程间通信

  1. Queue: 用 multiprocessing.Queue ,注意:这个Queue不能用在multiprocessing.Pool中
  2. Pipe: pipe只能适用于两个进程
  3. Manager: 提供进程间通信的多种数据类型,其中Manager().Queue()可以用在 multiprocessing.Pool中

协程

协程是什么:可以暂停的函数,且可以向暂停的函数传入值

协程的目的

  1. 采用同步的方式去编写异步的代码
  2. 使用单线程去切换任务

协程和多线程的差别

  1. 线程是由操作系统切换的;协程是单线程切换,而且可由程序猿自己去调度任务
  2. 不需要锁,并发性更高

生成器协程