使用 dict 和 set - 廖雪峰的官方网站 (liaoxuefeng.com)

用于学习记录,后期便于复习,参考链接

image.png

python file

image.png

image.png

调用脚本时会先载入 pyhton 解释器,然后运行脚本

rpm:软件管理包

操作符优先级:image.png

条件判断:

image.png

Python 为我们提供了非常完善的基础代码库,覆盖了网络、文件、GUI、数据库、文本等大量内容,被形象地称作 “内置电池(batteries included)”。用 Python 开发,许多功能不必从零编写,直接使用现成的即可。

语言定位:

Python 的定位是 “优雅”、“明确”、“简单”

Python 是解释型语言

image.png

# python

image.png

image.pngimage.png

image.png

# 数据类型

# int 整型

long int 长整型

int 整型

# float 浮点型

浮点数也就是小数,之所以称为浮点数,是因为按照科学记数法表示时

双精度浮点型 e

浮点型

# String 字符串

字符串是以单引号 ' 或双引号 " 括起来的任意文本

字符串是以 Unicode 编码

对于单个字符的编码,Python 提供了 ord() 函数获取字符的整数表示, chr() 函数把编码转换为对应的字符:

# Bool 布尔

True

Flase

# None 空值

None, None 不能理解为 0 ,因为 0 是有意义的,

Null 无意义

.

<iframe src="http://127.0.0.1:6806/widgets/brython-editor" data-src="http://127.0.0.1:6806/widgets/brython-editor" data-subtype="widget" border="0" frameborder="no" framespacing="0" allowfullscreen="true" style="width: 1341px; height: 276px;"></iframe>

# 变量

变量的概念基本上和初中代数的方程变量是一致的,

变量不仅可以是数字,还可以是任意数据类型。

变量名必须是大小写英文、数字和 _ 的组合,且不能用数字开头,字母或下划线开头

#变量类型
a=1
b='我是变量'
c=True
d=12.2e3
print(a,b,c,d)
print(type(a),type(b),type(c),type(d))

image.png

int a=1 静态语言 此时已经分配的 int 分区之后不能更改变量类型【不支持,Java】

a=3 动态语言,可以赋值成任意类型

# 动态定义

# 静态定义

a = 'ABC'
b = a
a = 'XYZ'
print(b)

执行 a = 'ABC' ,解释器创建了字符串 'ABC' 和变量 a ,并把 a 指向 'ABC'

py-var-code-1

执行 b = a ,解释器创建了变量 b ,并把 b 指向 a 指向的字符串 'ABC'

py-var-code-2

执行 a = 'XYZ' ,解释器创建了字符串 'XYZ',并把 a 的指向改为 'XYZ' ,但 b 并没有更改:

py-var-code-3

# 常量

所谓常量就是不能变的变量,通常用全部大写的变量名表示常量:

PI = 3.14159265359

# list 列表

list 是一种有序的集合,可以随时添加和删除其中的元素。

list=['a','b','c']
print(list)
print(len(list))

image.pngimage.png

# 切分

image.pngimage.png

# append()追加

str.append('a')

image.pngimage.png

# insert 插入指定位置

image.pngimage.png

# pop()删除末尾元素

要删除指定位置的元素,用 pop(i) 方法,其中 i 是索引位置

替换元素直接赋值即可

列表可以嵌套

s = ['python', 'java', ['asp', 'php'], 'scheme']

类型可以不同

L = ['Apple', 123, True]

# 切片

list [:-1] 不包含最后一个元素

list [:] 全部列表

list [::] 全部列表

image.pngimage.png

前 10 个数,每两个取一个

image.pngimage.png

# 列表生成

list(range(1,11))生成 10 个数 1-10

print([m+n for m in '123' for n in 'yza'])
k=[1,1,23,45,56,[1,12,3,467,[2,4,4,3,22]]]
print([x for x in k if x==1 ])

image.png

# tuple 元组

tuple 一旦初始化就不能修改

image.pngimage.png

但元组初始化后就不能进行更改了

b=(1)
#定义的不是 tuple,是 1 这个数!
# 这是因为括号 () 既可以表示 tuple,又可以表示数学公式中的小括号,
# 这就产生了歧义,因此,Python 规定,
# 这种情况下,按小括号进行计算,计算结果自然是 1
print(b)
print(type(b))
c=(1,)
print(c)
print(type(c))

image.png

image.png

# dict 字典

其他语言叫 map,使用键 - 值(key-value)存储,具有极快的查找速度。dict 的 key 必须是不可变对象。key 计算位置的算法称为哈希算法(Hash)。

# 定义

d={'name':'ruanyifen','age':60,'happy':'write'}
d['add']='Im add'
d['name']='fix'

image.png

# 取 value

# dict['key']

# 'key' in dict

# dict.get('key')

print(type(d))
print(d['name'])
print('name' in d)#方法一判断是否有这个主键在字典 d 中
print(d.get('name'))#方法二 取

image.png

# dict.pop ('key') 删除一个 key

image.png

# dict.keys 返回字典中所有 key 列表

# dict.update () 将 a 字典新 key,value 内容加入 b 字典中

dicta={"name":'ruan','age':20}
dictb={"name":'ruan2','age':40,'add':'w shi add'}
dictb.update(dicta)
print(dictb)

image.png

image.png

# 内建函数使用

type()

cmp()

len()

hash()

image.png

内建 cmp()函数比较两个 dict 时,先比较长度,后比值,输出 1 或 - 1

image.png

image.png

# dict 特点

dict 有以下几个特点:

  1. 查找和插入的速度极快,不会随着 key 的增加而变慢;
  2. 需要占用大量的内存,内存浪费多。

而 list 相反:

  1. 查找和插入的时间随着元素的增加而增加;
  2. 占用空间小,浪费内存很少。
    image.png
    image.png

# set 集合

也是一组 key 的集合,但不存储 value。由于 key 不能重复,所以,在 set 中,没有重复的 key。

重复元素在 set 中自动被过滤

# 定义

image.pngimage.png

image.pngimage.png

# set.add ('key') 添加元素

但重复元素不添加,自动去重

image.pngimage.png

# set.remove ('key') 删除元素

image.pngimage.png

set 可以看成数学意义上的无序和无重复元素的集合,

因此,两个 set 可以做数学意义上的交集、并集等操作:

# & 两个 set 交集

image.pngimage.pngimage.png

# | 两个 set 并集

image.pngimage.pngimage.png

image.png

# map () 的显示

打印 map 对象可以看到 map 对象返回的是一个地址,不是真实的数据

print(list(map对象))
print([it for it in map对象])

# 数据类型转换

# int()

# float()

# str()

# bool()

image.pngimage.png

# 条件判断

# if

if else

a=100
if a>=0:
    print(a)
else:
    print(-a)

image.png

if

if True:
    print('True')

image.png

if elif elif else

name='zhangsan'
if name=='zhangsan':
    print('我是',name)
elif name=='lisi':
    print('我是', name)
elif name=='wangwu':
    print('我是', name)
else:
    print('我谁的不是')

image.png

# input()输入输出

input () 返回的数据类型是 str

print()

# 循环 迭代

list,tuple,dict 都可循环

Python 的 for 循环本质上就是通过不断调用 next() 函数实现的,计算是惰性的

dict 循环按照 value 时:for value in dict.values

for value in d.values():
    print(value)

# for in

sum=0
for i in range(1,100):
    sum=sum+i
print(sum)

image.pngimage.pngimage.png

# while

sum2=0
k=0
while(k<100):
    sum2=sum2+k
    k=k+1
print(sum2)

image.png

# break

如果要提前结束循环,可以用 break 语句

# continue

通过 continue 语句,跳过当前的这次循环,直接开始下一次循环

# 生成器

在 Python 中,这种一边循环一边计算的机制,称为生成器:generator。

包括生成器和带 yield 的 generator function。

g = (x * x for x in range(10))

image.png

访问大文件

yield

# isinstance()迭代器

直接作用于 for 循环的对象统称为可迭代对象,都是迭代器 Iterable

listtupledictsetstr

listdictstr 虽然是 Iterable ,却不是 Iterator

listdictstrIterable 变成 Iterator 可以使用 **iter ()** 函数

以直接作用于 for 循环的数据类型有以下几种:

一类是集合数据类型,如 listtupledictsetstr 等;

一类是 generator ,包括生成器和带 yield 的 generator function。

可以使用 **isinstance ()** 判断一个对象是否是 Iterable 对象__iter__:

迭代对象

判断是不是可以迭代,用 Iterable

from collections import Iterable
isinstance({}, Iterable) --> True
isinstance((), Iterable) --> True
isinstance(100, Iterable) --> False

判断是不是迭代器,用 Iterator

from collections import Iterator
isinstance({}, Iterator)  --> False
isinstance((), Iterator) --> False
isinstance( (x for x in range(10)), Iterator)  --> True

Python 中 list,truple,str,dict 这些都可以被迭代,但他们并不是迭代器,为什么::因为和迭代器相比有一个很大的不同,list/truple/map/dict 这些数据的大小是确定的,也就是说有多少事可知的。但迭代器不是,迭代器不知道要执行多少次,所以可以理解为不知道有多少个元素,每调用一次 next (),就会往下走一步,是惰性的。

# 函数

抽象

将函数抽象成一个函数名称,不看内部结构直接调用方法

返回类型 函数名(输入参数):

函数体

# 调用函数

要调用一个函数,需要知道函数的名称和参数

绝对值 abs

image.pngimage.png

# 定义函数

def myabs(x):
    if x>0:
        return x
    if x<0:
        return -x
print(myabs(-100))

image.png

# 空函数

def nufun():
    pass

pass 可以用来作为占位符

# 函数 参数检查

def my_init_abs(x):
    if not isinstance(x,(int,float)):
        raise TypeError('no no no')
    else:
        if x>0:
            print(x)
        if x<0:
            print(-x)
my_init_abs(-90)

image.png

# 可返回多个值,函数

def return_much():
    a='返回'
    b='我也返回'
    c='我也要返回'
    return a,b,c
print(return_much())
print(type(return_much()))

image.png

# 函数参数

*args 是可变参数,args 接收的是一个 tuple;

**kw 是关键字参数,kw 接收的是一个 dict

power(x) 函数,参数 x 就是一个位置参数,可单个变量,list,set,tuple

power(*x) 函数,可传入单个变量,list,set,tuple,可以传入任意个参数或 0 个参数

power(**kw) 函数,字典 dict

可变参数允许你传入 0 个或任意个参数,这些可变参数在函数调用时自动组装为一个 tuple。

而关键字参数允许你传入 0 个或任意个含参数名的参数,这些关键字参数在函数内部自动组装为一个 dict.

power(x, n) ,用来计算 xn

power(x, n) 函数有两个参数: xn

默认参数,此时 age 和 city 为默认参数,可传值改变也可不变【不用传值】

power(L=None) 函数有 None 这个不变对象,可用 list

def enroll(name, gender, age=6, city='Beijing'):
    print('name:', name)
    print('gender:', gender)
    print('age:', age)
    print('city:', city)

image.png

# 必选参数

def a1(x):
    return x
print(a1(2))
print(a1(12.1))
print(a1('ruan'))
print(a1(True))
print(a1([1,2,3]))
print(a1({1,2,3}))
print(a1({"key":"vleaue",'name':'ruan','mun':23}))

image.png

# = 默认参数

def a2(x=9):
    return x
print(a2())
print(a2(2))
print(a2(12.1))
print(a2('ruan'))
print(a2(True))
print(a2([1,2,3]))
print(a2({1,2,3}))
print(a2({"key":"vleaue",'name':'ruan','mun':23}))

image.png

# * 可变参数

def a3(*x):
    return x
print(a3())
print(a3(2))
print(a3(12.1))
print(a3('ruan'))
print(a3(True))
print(a3([1,2,3]))
print(a3({1,2,3}))
print(a3({"key":"vleaue",'name':'ruan','mun':23}))

image.png

# ** 关键字参数

def a3(**kw):
    return kw
print(a3())
print(a3(kw=2))
print(a3(kw=12.1))
print(a3(kw='ruan'))
print(a3(kw=True))
print(a3(kw=[1,2,3]))
print(type(a3(kw=[1,2,3])))
print(a3(kw={1,2,3}))
print(type(a3(kw={1,2,3})))
print(a3(kw={"key":"vleaue",'name':'ruan','mun':23}))

image.png

# 递归函数

在函数内部,可以调用其他函数。

一个函数在内部调用自身本身,这个函数就是递归函数

使用递归函数需要注意防止栈溢出

#递归函数
def funmyself(x):
    if x>1:
        return x+funmyself(x-1)
    elif x==1:
        return 1
print(funmyself(3))

image.png

解决栈溢出方法:

尾递归优化,事实上尾递归和循环的效果是一样的

尾递归是指,在函数返回的时候,调用自身本身,并且,return 语句不能包含表达式

#尾递归
def funmyself2(n):
    return  funmyself2_it(n,1)
def  funmyself2_it(n,pro):
    if n==1:
        return pro
    else:
        return funmyself2(n-1)+n
print(funmyself2(100))

image.png

此时 funmyself2 是尾递归函数

# 转义字符 \

转义字符 \ 可以转义很多字符,比如

\n 表示换行,

\t 表示制表符,

字符 \ 本身也要转义,所以 \\ 表示的字符就是 \

Python 还允许用 r'' 表示 '' 内部的字符串默认不转义

# 运算符 and、or 和 not

运算优先级:not>or>and

# 除法 ///

print(10/3)
print(10//3)

# 除法一 / 浮点数

# 除法二 // 地板除 整数

/ 除法计算结果是浮点数,即使是两个整数恰好整除,结果也是浮点数:

// ,称为地板除,两个整数的除法仍然是整数:

# 取余 %

print(10%1)
print(10%3)
print(4%7)
print(2%20)
#如果 a% b a>b 则结果为 a

image.png

# 字符编码

ASCII 编码

8 个比特(bit)作为一个字节(byte)

一个字节能表示的最大的整数就是 255(二进制 11111111 = 十进制 255)

两个字节可以表示的最大整数是 65535 ,4 个字节可以表示的最大整数是 4294967295

大写字母 A 的编码是 65 ,二进制的 01000001 ,小写字母 z 的编码是 122

Unicode 把所有语言都统一到一套编码里,这样就不会再有乱码问题

ASCII 编码是 1 个字节,而 Unicode 编码通常是 2 个字节

ASCll 出现乱码问题引入 Unicode 编码存储空间多了一倍引入 UTF-8 编码

utf-8:将 Unicode 字符根据不同的数字大小编码成 1-6 个字节,常用的英文字母被编码成 1 个字节,汉字通常是 3 个字节,

字符ASCIIUnicodeUTF-8
A0100000100000000 0100000101000001
x01001110 0010110111100100 10111000 10101101

在计算机内存中,统一使用 Unicode 编码,当需要保存到硬盘或者需要传输的时候,就转换为 UTF-8 编码。

用记事本编辑的时候,从文件读取的 UTF-8 字符被转换为 Unicode 字符到内存里,编辑完成后,保存的时候再把 Unicode 转换为 UTF-8 保存到文件

image.png

浏览网页的时候,服务器会把动态生成的 Unicode 内容转换为 UTF-8 再传输到浏览器:

web-utf-8

# compile () 字符串编译为字节代码

# 编码转化

# ord ('A') 字母转字符

# chr (65) 字符转字母

a=ord('A')
b=chr(65)
print(a,b)

image.png

# b'str' 转为字节类型 bytes

bytes 类型的数据用带 b 前缀的单引号或双引号表示

x = b'ABC'

要注意区分 'ABC'b'ABC' ,前者是 str ,后者虽然内容显示得和前者一样,但 bytes 的每个字符都只占用一个字节。

image.pngimage.png

# str.encode('ascii') str 变为 bytes

ASCII

UTF-8

image.pngimage.png

# str.decode('utf-8') bytes 变为 str

print(type(b'abc'))
print(b'abc'.decode('utf-8'))
print(type(b'abc'.decode('utf-8')))

image.pngimage.png

len(str)计算字符数

函数计算的是 str 的字符数,如果换成 byteslen() 函数就计算字节数

print(len('abc'))
print(len(b'abc'))
print(len('中'))
print(len('中'.encode('utf-8')))

image.pngimage.png

# 特殊注释

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

第一行注释是为了告诉 Linux/OS X 系统,这是一个 Python 可执行程序,Windows 系统会忽略这个注释

第二行注释是为了告诉 Python 解释器,按照 UTF-8 编码读取源代码,否则,你在源代码中写的中文输出可能会有乱码。

# 占位符 格式化

# 占位符 % s % d % f

image.png

格式化方式和 C 语言是一致

% 运算符就是用来格式化字符串的。

在字符串内部,

%s 表示用字符串替换,

%d 表示用整数替换,

有几个 %? 占位符,后面就跟几个变量或者值,顺序要对应好。如果只有一个 %? ,括号可以省略

image.pngimage.png

占位符替换内容
%d整数
%f浮点数
%s字符串
%x十六进制整数

image.pngimage.png

转义: %% 来表示一个 %

# format()格式化字符串

image.png

image.png

# f-string 格式化字符串

{r} 被变量 r 的值替换, {s:.2f} 被变量 s 的值替换,并且 : 后面的 .2f 指定了格式化参数(即保留两位小数),因此, {s:.2f} 的替换结果是 19.62

image.pngimage.png

# 函数式编程

函数是 Python 内建支持的一种封装,通过层层函数进行调用

#面向过程的程序设计 #:把复杂任务分解成简单的任务,这种分解可以称之为面向过程的程序设计

函数式和函数的区别:

对比例子:计算和计算器的区别

编程语言,就是越低级的语言,越贴近计算机,抽象程度低,执行效率高,比如 C 语言;越高级的语言,越贴近计算,抽象程度高,执行效率低,比如 Lisp 语言

Python 不是纯函数式编程语言

函数式编程就是一种抽象程度很高的编程范式,

纯粹的函数式编程语言编写的函数没有变量,因此,任意一个函数,只要输入是确定的,输出就是确定的

# 函数式编程特点:

  1. 纯函数式编程语言函数没有变量,输入输出确定
  2. 允许本身作为参数传入另一个函数,允许返回一个函数

# 高阶函数

参数中有函数

高阶函数除了可以接受函数作为参数外,还可以把函数作为结果值返回

# 变量可以指向函数

a = 函数

求绝对值的函数 abs() 为例

print(abs(-10))
print(abs)

image.png

abs(-10)是函数调用,abs 是函数本身

k=abs(-20)
print(k)
#函数本身也可以赋值给变量
h=abs
print(h)
print(h(-100))

image.png

结论:函数本身也可以赋值给变量,即:# 变量可以指向函数。#

# 函数名也是变量

#函数名 #:其实就是指向函数的变量

a () 中 a 是指向函数 a()的变量

# 传入函数

既然变量可以指向函数,函数的参数能接收变量,那么一个函数就可以接收另一个函数作为参数,这种函数就称之为高阶函数。

#高阶函数 #:一个函数就可以接收另一个函数作为参数

b()

a(b)

x=a

def f(x):
    return abs(int(x))
def a(a,b,f):
    return f(a)+f(b)
if __name__ == '__main__':
    a1=input('a')
    a2=input('b')
    print(a(a1,a2,f))

image.png

此时函数 a 为高阶函数,需要调用 f 函数作为参数

# map/reduce 内建函数

内建了 map()reduce() 函数 高阶函数

# map()函数处理生成新 Iterator 迭代器

image.png 两个参数,函数名【函数本身】,需要处理的编程式 iterator

<br /> 创建一个迭代器,使用每个迭代器中的参数计算函数。当最短迭代用尽时停止。

map(func, *iterables) --> map object
def f(x):
    return x*x
r=map(f,[1,2,3,4,4,4,4,4,4,4,4])
print(r)
print(type(r))
print(list(r))
print(type(list(r)))

image.png

运算规则抽象

# reduce()函数作用在序列上

image.png

两个参数,函数名【函数本身】,需要处理的 #序列 #: sequence (序列) 是一组有顺序的元素的集合

序列基本样式 [下限:上限:步长]

reduce 把结果继续和序列的下一个元素做累积计算

reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)
from functools import reduce
>>> def fn(x, y):
...     return x * 10 + y
...
>>> reduce(fn, [1, 3, 5, 7, 9])
13579

image.png

# filter () 过滤序列

参数和 map()相似

filter() 也接收一个函数和一个序列

# sorted()排序

高阶函数image.png

参数:排序对象,key = 函数

sorted([36, 5, -12, 9, -21], key=abs)

排序的核心是比较两个元素的大小

print(sorted([1,2,353,6,3,234,43,435]))

image.png

key 指定绝对值大小排序

print(sorted([1,2,353,6,3,234,43,435,-242,-34,34,35],key=abs))

image.png

# 返回函数

# 函数作为返回值

高阶函数除了可以接受函数作为参数外,还可以把函数作为结果值返回

# 如果不需要立刻求和,而是在后面的代码中,
# 根据需要再计算怎么办?可以不返回求和的结果,而是返回求和的函数:
def zary_sum(a):
    def sum():
        sum1=0
        for i in a:
            sum1=sum1+i
        return sum1
    return sum
print(type(zary_sum([1,2,3,4])))
f=zary_sum([1,2,3,4])
print(f())

image.png

调用返回函数时,每次调用都会新生成一个函数

image.png

# 闭包

当一个函数的返回值是另外一个函数,

而返回的那个函数如果调用了其父函数内部的其它变量,如果 返回的这个函数在外部被执行,就产生了闭包

返回函数中,返回的函数调用父函数的内部变量

image.png

#返回函数
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(),f2(),f3())

image.png

返回一个函数时,牢记该函数并未执行,返回函数中不要引用任何可能会变化的变量。

# lambda()匿名函数

lambda 关键字 函数参数:函数表达式

传入函数时,有些时候,不需要显式地定义函数

Python 对匿名函数的支持有限,只有一些简单的情况下可以使用匿名函数。

lambda x:x*x
#等价于
def f(x):
   return x*x

关键字 lambda 表示匿名函数,冒号前面的 x 表示函数参数,只能一个表达式

不用写 return ,返回值就是该表达式的结果。

匿名函数也是一个函数对象

f=lamdba x:x*x

判断奇数函数

原函数:

def is_odd(n):
    return n % 2 == 1
L = list(filter(is_odd, range(1, 20)))

采用匿名函数修改

l=list(filter(lambda x:x%2==1,range(1,20)))
print(l)

image.png

# 装饰器 Decorator

# 本质上,装饰器就是一个返回函数的高阶函数

@log 等价于

now = log(now)

由于函数也是一个对象,而且函数对象可以被赋值给变量,

所以,通过变量也能调用该函数

image.pngimage.png

def log(func):
    def wrapper(*args,**kwargs):
        print('call %s'% func.__name__)
        return func(*args,**kwargs)
    return wrapper()
#func 为参数所以是高阶函数
#return 函数所以是返回函数,
#没有调用父函数中参数,所以不是闭包

image.pngimage.png

场景注意:

无 @装饰器时函数不调用,需要参数才调用

当 @时会直接调用装饰器定义函数然后执行函数,不用调用函数

三层时,传入参数

def log1(text):
    def decorator(func):
        def wapper(*args,**kw):
            print('%s %s'%(text,func.__name__))
            return func(*args,**kw)
        return wapper
    return decorator
@log1('ruan')
def now3():
    print("hhh")
now3()

相当于在返回高阶函数上还有一个函数,所以返回时应该还要调用一次

# @wraps 常用装饰器

当装饰器是个闭包时,装饰器调用变量会改变增加 @wraps 后装饰器内的变量不变

装饰器在装饰一个函数时,,原函数就成了一个新的函数,也

就是说其属性会发生变化,所以为了 不改变原函数的属性

我们会调用 functools 中的 wraps 装饰器来保证原函数的属性不变

# 不加 wraps 时

@wraps(func)
from functools import wraps
def wrap(func):
   
    def b():
        'b'
        print('decorator:',b.__name__)
        print('funname',func.__name__)
        func()
    return b
@wrap
def a():
    'a'
    print('name',a.__name__)
a()

image.png

加装饰器 wraps 时

from functools import wraps
def wrap(func):
    @wraps(func)
    def b():
        'b'
        print('decorator:',b.__name__)
        print('funname',func.__name__)
        func()
    return b
@wrap
def a():
    'a'
    print('name',a.__name__)
a()

image.png

闭包的概念:调用父函数中的变量的函数,为了保证数据安全。变量作用域只在函数内部,可在闭包中操作数据。

装饰器返回为什么是函数名(函数内存地址)而不直接执行函数?

当有参数传入时,可直接与调用的函数中的值传入参数执行。

()是运算符 f () 与 f.call () 等价:将 f 对象变成变成可调用的对象

# 偏函数(functools 模块)

属于 functools 模块

# 作用:

通过设定参数的默认值,降低函数调用的参数

int() 函数默认按十进制转换

print(int('100',base=8))

经常调用于是重写一个函数 int2

def int2(x, base=8):
    print(int(x, base))
    return int(x, base)
print(int2('2334'))

采用偏函数

import functools
int3=functools.partial(int,base=8)
print(int3('46'))
print(int())

image.png

functools.partial 的作用是将函数的特定参数固定住(设定为默认值)

image.png

创建偏函数的时候也可以接收,函数对象,*args,**kw

# 模块

python 包:作用区分相同名称的模块

模块相当于一个 py 文件

image.png

image.png

# 作用域

仅仅在模块内部使用。在 Python 中,是通过 _ 前缀来实现的。

# pubilc 公开

正常的函数和变量名是公开的(public)

# private 非公开_,__

_xxx 和__xxx 这样的函数或变量就是非公开的(private)

# 安装第三方模块 pip

pip install 模块名

# 模块搜索路径

import sys
print(sys.path)

两种方式:

  1. 添加搜索路径
    import sys
    sys.path.append('/Users/michael/my_py_scripts')
  2. 设置环境变量

第二种方法是设置环境变量 PYTHONPATH

# 面向对象编程

面向对象编程 ——Object Oriented Programming,简称 OOP,是一种程序设计思想

对象作为程序的基本单元,

一个对象包含了数据和操作数据的函数

数据封装、继承和多态是面向对象的三大特点

# 类和实例

面向对象最重要的概念就是类(Class)和实例(Instance)

类是抽象出来的模板

实例是根据类创建出的对象,每个对象可能有属性和方法

定义类是通过 class 关键字,类名通常是大写开头的单词

class Student(object):
    pass

image.png

!!!在类中定义函数有一点不同,定义佛如方法第一个参数永远是实例变量本身 self

仍然可以用默认参数、可变参数、关键字参数和命名关键字参数

# 数据封装

class Student(object):
    def __init__(self, name, score):
        self.name = name
        self.score = score
    def get_grade(self):
        if self.score >= 90:
            return 'A'
        elif self.score >= 60:
            return 'B'
        else:
            return 'C'

image.png

# 访问限制

# 作用:

确保了外部代码不能随意修改对象内部的状态

实例的变量名如果以 __ 开头,就变成了一个私有变量(private)

外部无法访问_name

class Student(object):
    def __init__(self,name,age):
        self._name=name
        self.age=age
    def print_name(self):
        print(self._name)
        return self.age,self._name
a=Student('ruan',23)
h=a.print_name()
print(h)

image.png

若是要获取,修改变量增加 get,set 方式即可

class Student(object):
    def __init__(self,name,age):
        self._name=name
        self.age=age 
    def get_name(self):
        return self.name
    def set_name(self,name):
        self._name=name

Python 本身没有任何机制阻止你干坏事,一切全靠自觉。

类外部无法访问

# 继承和多态

# 继承

# 多态

在继承关系中,如果一个实例的数据类型是某个子类,那它的数据类型也可以被看做是父类。

比如:动物是父类,狗和鱼是子类;鱼是鱼类,鱼是动物都成立。

判断一个变量是否是某个类型可以用 isinstance() 判断

# 鸭子类型

并不要求严格的继承体,一个对象只要 “看起来像鸭子,走起路来像鸭子”,那它就可以被看做是鸭子。

# 获取对象信息

# type()判断对象类型

image.png

# isinstance () 对于继承关系,判断 class 的类型

# dir()获取对象的所有属性和方法

# len()对象长度

# lower()返回小写的字符串

# getattr()获取属性 a

# setattr()设置属性 a

# hasattr(obj,'a')判断是否有属性 a

getattr(obj, 'z', 404) # 获取属性 'z',如果不存在,返回默认值 404
404

# 实例属性和类属性

image.png

class Student(object):
    name='ruan'
   
h=Student()
h.name='hhh'

类中的 name 是类属性,

创建 h 类对象即实例后赋值的是实例属性 name,但由于实例对象的优先级比类属性高,会屏蔽类中的 name 属性,即 h.name 的值为 hhh

# 总结:

  1. 实例属性属于各个实例所有,互不干扰;
  2. 类属性属于类所有,所有实例共享一个属性;
  3. 不要对实例属性和类属性使用相同的名字,否则将产生难以发现的错误

# 面向对象高级编程

数据封装、继承和多态只是面向对象程序设计中最基础的 3 个概念

多重继承、定制类、元类

# _slots_使用

可以给创建的实例绑定属性和方法

给一个实例绑定的方法对另外一个实例对象是不起作用的

class A:
    def run(self):
        print("i im ferther runing....")
sun1=A()
#给实例 sun1 设置 name 属性
sun1.name='i im name'
#创建实例对象 2
sun2=A()
#实例对象 sun1 的属性和 sun2 无关,即 sun2 没有 name 属性
#给实例 sun1 绑定方法,方法和属性同理
#定义方法
def setAll(self,num):
    print(num)
sun1.newfun=MethodType(setAll, sun1)
sun1.newfun(37)
#若所有实例都需要绑定方法则给类绑定方法
A.setAll=setAll
#给类绑定方法后,所有创建的实例的均可调用
def set_age(self, age): # 定义一个函数作为实例方法
...     self.age = age
...
>>> from types import MethodType
>>> s.set_age = MethodType(set_age, s) # 给实例绑定一个方法
>>> s.set_age(25) # 调用实例方法

# 限制实例属性,定义一个特殊的 __slots__ 变量

class Student(object):
    __slots__ = ('name', 'age') # 用 tuple 定义允许绑定的属性名称
s=Student()
s.name='ruan'
s.firstname='i im firstname'
#输出的时候 firstname 的属性会报错,
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Student' object has no attribute 'firstaname'

# 注意:

_slots_使用时要注意,定义的属性只在当前的类的实例中,对于继承的子类是不起作用的

class People(object):
    __slots__ = ('name','age')
    def run(self):
        print('i im run people......')
class Teacher(People):
    def say(self):
        print('i im teacher....')
t=Teacher()
t.tall='shouhua'
print(t.tall)
p=People()
p.tall('shouhuap')
print(p.tall)

image.png

只限制父类 People 的属性,而子类 Teacher 中不限制

# @property

在绑定属性时,如果我们直接把属性暴露出去,导致可以随意更改。通过 get,set 来获取更改属性值。

在 python 中直接调用装饰器将一个方法变成属性调用

class Student(object):
    @property
    #使用 get 方法是调用装饰器 @peoperty,
    # 同时自动创建了另一个装饰器 @属性.setter
    def score(self):
        return self.score
    @score.setter
    def score(self,value):
        self._score=value

# 总结:

- 权限限制只对类对象实际起作用,想要达到方法和属性强制访问权限,需要使用 @property 装饰器进行 get,set 方法

属性名与方法名一定要区分开,不然会进入死循环(self._age,def age ())
实例化的对象使用属性时,不是调用属性(meizi._age),而是用的方法名(meizi.age)
@property 其实就是实现了 getter 功能; @xxx.setter 实现的是 setter 功能;还有一个 @xxx.deleter 实现删除功能
定义方法的时候 @property 必须在 @xxx.setter 之前,且二者修饰的方法名相同(age ())
如果只实现了 @property(而没有实现 @xxx.setter),那么该属性为 只读属性

#请利用 @property 给一个 Screen 对象加上 width 和 height 属性,
# 以及一个只读属性 resolution:
class Screen(object):
    __slots__ = ('_width','_height','_resolution')
    @property
    def width(self):
        return self._width
    # 方法名称和实例变量均为 width:
    @width.setter
    def width(self,widthValue):
        self._width=widthValue
    @property
    def height(self):
        return self._height
    @width.setter
    def height(self, height):
        self._height = height
    @property
    def resolution(self):
        return self._width * self._height
s=Screen()
s.width=23
s.height=12
print(s.resolution)

image.png

s.score = 60 # OK,实际转化为 s.set_score (60)
s.score # OK,实际转化为 s.get_score ()

要特别注意:属性的方法名不要和实例变量重名。例如,以下的代码是错误的:

class Student(object):

    # 方法名称和实例变量均为birth:
    @property
    def birth(self):
        return self.birth

出现递归调用错误

image.png

之前的例子中 width 和_width 不同所以可以运行

# 多重继承

image.png

python 可以支持多继承,即一个子类可以继承多个父类;但 java 是单继承,只能有一个父类

Tercher(Name,study,teach)即 Teacher 可以继承多个父类

# MixIn

在设计类的继承关系时,通常,主线都是单一继承下来的,例如, Teacher 继承自 Name。但是,如果需要 “混入” 额外的功能,通过多重继承就可以实现,比如 Teacher 除了继承自 Name 外,再同时继承 Teach 。这种设计通常称之为 MixIn

Python 自带了 TCPServerUDPServer 这两类网络服务,而要同时服务多个用户就必须使用多进程或多线程模型,这两种模型由 ForkingMixInThreadingMixIn 提供。

# 多继承

多重继承这个名词一般用来形容继承链条可以很长,多个层次。

# 多重继承

多继承则指一个类可以有多个基类,相反则是单继承。任何面向对象编程语言都支持多重继承,但像 java 这种只能通过接口实现有限程度的多继承

问:多继承 如果多个类有共同得方法名 怎么区分是调得哪个类🤡

答:调用该方法的时候,会调用第一顺位继承父类的方法

# 总结:

  1. Python 允许使用多重继承,因此,MixIn 就是一种常见的设计
  2. 只允许单一继承的语言(如 Java)不能使用 MixIn 的设计

# 定制类

Python 的 class 中还有__xxx__有特殊用途的函数,可以帮助我们定制类

# str () 回用户看到的字符串

将对象 <__main__.Student object at 0x109afb190> 变成易读的数据

只在调用 print 时会调用__str__,交互界面时还是现实上方不易读的对象内容,此时用

# repr () 返回程序开发者看到的字符串

__str__() 返回用户看到的字符串,而 __repr__() 返回程序开发者看到的字符串,

也就是说, __repr__() 是为调试服务的

简写

def __str__(self):
        return 'xxx object (name=%s)' % self.name
__repr__ = __str__

# _iter () 返回一个迭代对象

需要用到 for in 迭代,需要转化为迭代对象

该方法返回一个迭代对象,然后,Python 的 for 循环就会不断调用该迭代对象的 __next__() 方法拿到循环的下一个值,直到遇到 StopIteration 错误时退出循环

例子:

class Fib(object):
    def __init__(self):
        self.a,self.b=0,1
    def __iter__(self):
        return self
    def __next__(self):
        self.a,self.b=self.b,self.a+self.b
        if self.a>1000:
            raise  StopIteration
        return self.a
a=Fib()
for i in a:
    print(i)

image.png

# getitem () 表现得像 list 那样按照下标取出元素

class Fib(object):
    def __init__(self):
        self.a,self.b=0,1
    def __iter__(self):
        return self
    def __next__(self):
        self.a,self.b=self.b,self.a+self.b
        if self.a>1000:
            raise  StopIteration
        return self.a
    def __getitem__(self, item):
        a,b=1,1
        for i in range(item):
            a,b=b,a+b
        return a
a=Fib()
print(a[3])

image.png

以上是传入 int,切片功能实现,isinstance 判断类型

class Fib(object):
    def __init__(self):
        self.a,self.b=0,1
    def __iter__(self):
        return self
    def __next__(self):
        self.a,self.b=self.b,self.a+self.b
        if self.a>1000:
            raise  StopIteration
        return self.a
    def __getitem__(self, item):
        if isinstance(item,int):
            a, b = 1, 1
            for i in range(item):
                a, b = b, a + b
            return a
        if isinstance(item,slice):
            start=item.start
            stop=item.stop
            if start is None:
                start=0
            a,b=1,1
            L=[]
            for x in range(stop):
                L.append(a)
                a,b=b,a+b
        return L
a=Fib()
print(a[3:12])

image.png

# getattr () 动态返回一个属性

调用类属性或方法时,先在__init__() 获取后,再从__getattr__() 获取,获取不到才报错

# call () 直接调用实例本身

与直接调用这个函数一样

class People(object):
    def __init__(self,name):
        self.name=name
    def __call__(self, *args, **kwargs):
        print('i im call %s'% self.name)
p=People('ruan')
p()

image.png

# 使用枚举类

枚举类:在某些情况下,一个类的 实例对象 的数量有限且固定 的,如季节类,它的实例对象只有春、夏、秋、冬。 在 Java 中像这种对象实例有限且固定的类被称为枚举类;这样的枚举类型定义一个 class 类型,然后,每个常量都是 class 的一个唯一实例。Python 提供了 Enum 类来实现这个功能。

from enum import Enum
M=Enum('a',('sun1','sun2','sun3','sun4'))
print(M.sun1)

image.png

自定义枚举类

from enum import Enum,unique
@unique
class Week(Enum):
    sun1=1
    sun2=2
    sun3=3
day2=Week.sun2
print(day2)

image.png

from enum import Enum,unique
@unique
class Gender(Enum):
    Male=0
    Female=1
class Student(object):
    def __init__(self,name,gender):
        self.name=name
        self.gender=gender
# 测试:
bart = Student('Bart', Gender.Male)
if bart.gender == Gender.Male:
    print('测试通过!')
else:
    print('测试失败!')

image.png

# 使用元类 [创建类]

实例对象是类创建

类是元类创建

创建类的方式

# 方式一:type()

type() 函数既可以返回一个对象的类型,又可以创建出新的类型,比如,我们可以通过 type() 函数创建出 Hello

from class1104 import *
h=Hello()
print(type(h))
print(type(Hello))

image.png

Hello = type('Hello', (object,), dict(hello=fn))

要创建一个 class 对象, type() 函数依次传入 3 个参数:

  1. class 的名称;
  2. 继承的父类集合,注意 Python 支持多重继承,如果只有一个父类,别忘了 tuple 的单元素写法;
  3. class 的方法名称与函数绑定,这里我们把函数 fn 绑定到方法名 hello

# 方式二:元类 metaclass

先定义 metaclass,然后创建类。

先定义类,然后创建实例。

metaclass 是 Python 面向对象里最难理解,也是最难使用的魔术代码。

按照默认习惯,metaclass 的类名总是以 Metaclass 结尾,以便清楚地表示这是一个 metaclass

# metaclass 采用 type 创建类 ,metaclass 是类的模板,所以必须从 `type` 类型派生
class ListMetaclass(type):
    def __new__(cls, name,bases,attrs):
        attrs['add']=lambda self, value:self.append(value)
        return type.__new__(cls,name,bases,attrs)
class MyList(list,metaclass=ListMetaclass):
    pass
mylist=MyList()
mylist.add(1)
print(mylist)

image.png

__new__() 方法接收到的参数依次是:

  1. 当前准备创建的类的对象;
  2. 类的名字;
  3. 类继承的父类集合;
  4. 类的方法集合

# 应用场景

ORM 全称 “Object Relational Mapping”,即对象 - 关系映射,就是把关系数据库的一行映射为一个对象,也就是一个类对应一个表,这样,写代码更简单,不用直接操作 SQL 语句。

要编写一个 ORM 框架,所有的类都只能动态定义,因为只有使用者才能根据表的结构定义出对应的类来。

# 错误处理 try

try:
    print('try...')
    r = 10 / 0
    print('result:', r)
except ValueError as e:
    print('ValueError:', e)
except ZeroDivisionError as e:
    print('ZeroDivisionError:', e)e)
finally:
    print('finally...')
print('END')

Python 的错误其实也是 class,所有的错误类型都继承自 BaseException

UnicodeErrorValueError 的子类🤡

Built-in Exceptions — Python 3.10.0 documentation

# 调用栈

让 Python 解释器来打印出错误堆栈

image.png

# 记录错误 logging

可将 logging 生成一个 txt 方便查看

try:
        xxx
    except Exception as e:
        logging.exception(e)

# 抛出错误 raise

except ValueError as e:
        print('ValueError!')
        raise

bar() 函数中,我们明明已经捕获了错误,但是,打印一个 ValueError! 后,又把错误通过 raise 语句抛出去了,这不有病么?

其实这种错误处理方式不但没病,而且相当常见。捕获错误目的只是记录一下,便于后续追踪。但是,由于当前函数不知道应该怎么处理该错误,所以,最恰当的方式是继续往上抛,让顶层调用者去处理。

# 调试方法

# 1. print()

# 2. 断言 assert

assert n != 0, 'n is zero!'

assert 的意思是,表达式 n != 0 应该是 True ,否则,根据程序运行的逻辑,后面的代码肯定会出错。

采用断言的好处:

启动 Python 解释器时可以用 -O 参数来关闭 assert

$ python -O err.py

image.png

关闭后,你可以把所有的 assert 语句当成 pass 来看

# 3. logging

import logging
s = '0'
n = int(s)
logging.info('n = %d' % n)
print(10 / n)

# 4.pbd 单步执行

启动 Python 的调试器 pdb,让程序以单步方式运行,可以随时查看运行状态。

python -m pdb xxx.py
(Pbd) 1#查看第一行代码,单步执行第一行代码

# 5. pdb.set_trace()

这个方法也是用 pdb,但是不需要单步执行,我们只需要 import pdb ,然后,在可能出错的地方放一个 pdb.set_trace() ,就可以设置一个断点:

import pdb
s = '0'
n = int(s)
pdb.set_trace() # 运行到这里会自动暂停
print(10 / n)

可以用命令 p 查看变量,或者用命令 c 继续运行:

image.png

# 6.IDE 工具

vscode,pycharm....

# 单元测试

单元测试是用来对一个模块、一个函数或者一个类来进行正确性检验的测试工作。

# 文档测试

doctest 非常有用,不但可以用来测试,还可以直接作为示例代码。通过某些文档生成工具,就可以自动把包含 doctest 的注释提取出来。用户看文档的时候,同时也看到了 doctest。

Python 内置的 “文档测试”(doctest)模块可以直接提取注释中的代码并执行测试.

class Dict(dict):
    """"
      这一段就是文档测试
       Simple dict but also support access as x.y style.
       >>> d1 = Dict()
       >>> d1['x'] = 100
       >>> d1.x
       100
       >>> d1.y = 200
       >>> d1['y']
       200
       >>> d2 = Dict(a=1, b=2, c='3')
       >>> d2.c
       '3'
       >>> d2['empty']
       Traceback (most recent call last):
           ...
       KeyError: 'empty'
       >>> d2.empty
       Traceback (most recent call last):
           ...
       AttributeError: 'Dict' object has no attribute 'empty'
       """
    def __init__(self, **kw):
        super(Dict, self).__init__(**kw)
    def __getattr__(self, key):
        try:
            return self[key]
        except KeyError:
            raise AttributeError(r"'Dict' object has no attribute '%s'" % key)
    def __setattr__(self, key, value):
        self[key] = value
if __name__ == '__main__':
    import doctest
    doctest.testmod()

将其中一个函数注释,运行让它报错

image.png

# IO 编程

程序和运行时的数据在内存中驻留

涉及到数据交换的地方,通常是磁盘、网络等,就需要 IO 接口

通常,程序完成 IO 操作会有 Input 和 Output 两个数据流

Stream(流)是一个很重要的概念,可以把流想象成一个水管,数据就是水管里的水,但是只能单向流动。

在 IO 编程中,就存在速度严重不匹配的问题。举个例子来说,比如要把 100M 的数据写入磁盘,CPU 输出 100M 的数据只需要 0.01 秒,可是磁盘要接收这 100M 数据可能需要 10 秒,怎么办呢?有两种办法:

image.png

# 同步 IO

第一种是 CPU 等着,也就是程序暂停执行后续代码,等 100M 的数据在 10 秒后写入磁盘,再接着往下执行,这种模式称为同步 IO;

# 异步 IO

另一种方法是 CPU 不等待,只是告诉磁盘,“您老慢慢写,不着急,我接着干别的事去了”,于是,后续代码可以立刻接着执行,这种模式称为异步 IO。

如果是服务员跑过来找到你,这是回调模式,如果服务员发短信通知你,你就得不停地检查手机,这是轮询模式。总之,异步 IO 的复杂度远远高于同步 IO。

# 文件读写

# 读文件 open()

传入文件名,标示符

参数:'rb' 二进制

encoding='gbk' 字符编码

f = open('/Users/michael/test.txt', 'r')

# read () 一次读取全部内容

f.read()
'Hello, world!'

# f.close()关闭文件

简化方法

# with open('filepath', 'r') as f: print(f.read())

Python 引入了 with 语句来自动帮我们调用 close() 方法,并且不必调用 f.close() 方法

with open('/path/to/file', 'r') as f:
    print(f.read())

如果文件很小, read() 一次性读取最方便;

如果不能确定文件大小,反复调用 read(size) 比较保险;

如果是配置文件,调用 readlines() 最方便

for line in f.readlines():
    print(line.strip()) # 把末尾的 '\n' 删掉

file 和缓存时 = 是 file-like Object 对象,不要求从特定类继承,只要写个 read() 方法就行

# f.write () 写文件

f = open('/Users/michael/test.txt', 'w')
f.write('Hello, world!')
f.close()

with open('/Users/michael/test.txt', 'w') as f:
    f.write('Hello, world!')

使用 with 语句操作文件 IO 是个好习惯

# StringIO 和 BytesIO

# StringIO

StringIO 顾名思义就是在内存中读写 str

from io import StringIO
 f = StringIO()
f.write('hello')

getvalue() 方法用于获得写入后的 str

from io import StringIO
>>> f = StringIO('Hello!\nHi!\nGoodbye!')
>>> while True:
...     s = f.readline()
...     if s == '':
...         break
...     print(s.strip())

# BytesIO

操作二进制数据,就需要使用 BytesIO

>>> from io import BytesIO
>>> f = BytesIO()
>>> f.write('中文'.encode('utf-8'))
6
>>> print(f.getvalue())
b'\xe4\xb8\xad\xe6\x96\x87'

# os 模块

# os.name 操作系统类型

# os.uname () 详细系统信息

# os.enciron 环境变量

要获取某个环境变量的值,可以调用 os.environ.get('key')

# 查看当前目录的绝对路径:
>>> os.path.abspath('.')
'/Users/michael'
# 在某个目录下创建一个新目录,首先把新目录的完整路径表示出来:
>>> os.path.join('/Users/michael', 'testdir')
'/Users/michael/testdir'
# 然后创建一个目录:
>>> os.mkdir('/Users/michael/testdir')
# 删掉一个目录:
>>> os.rmdir('/Users/michael/testdir')

通过 os.path.join() 函数,这样可以正确处理不同操作系统的路径分隔符

# os.path.join( ) 连接路径

# os.path.split() 拆分路径

# os.path.splitext() 文件扩展名

# 对文件重命名:
>>> os.rename('test.txt', 'test.py')
# 删掉文件:
>>> os.remove('test.py')

shutil 模块提供了 copyfile() 的函数,它们可以看做是 os 模块的补充

最后看看如何利用 Python 的特性来过滤文件。比如我们要列出当前目录下的所有目录,只需要一行代码:

>>> [x for x in os.listdir('.') if os.path.isdir(x)]
['.lein', '.local', '.m2', '.npm', '.ssh', '.Trash', '.vim', 'Applications', 'Desktop', ...]

要列出所有的 .py 文件,也只需一行代码:

>>> [x for x in os.listdir('.') if os.path.isfile(x) and os.path.splitext(x)[1]=='.py']
['apis.py', 'config.py', 'models.py', 'pymonitor.py', 'test_db.py', 'urls.py', 'wsgiapp.py']

# 序列化 pickle 模块

变量从内存中变成可存储或传输的过程称之为序列化,Python 中叫 pickling

变量内容从序列化的对象重新读到内存里称之为反序列化,即 unpickling

# pickle.dumps () 对象 -》字节 [序列化]

pickle.dumps() 方法把任意对象序列化成一个 bytes

pickle.dumps() 方法把任意对象序列化成一个 bytes , 并写入文件中

import pickle
d=dict(name='ruan',age=34,freand='woman')
# print(pickle.dumps(d))
f = open('timezone.txt', 'wb')
pickle.dump(d, f)
f.close()

# pickle.load () 字节 -》对象【反序列化】

import pickle
f=open(r'C:\Users\yangs\PycharmProjects\python_study\fun\timezone.txt','rb')
d=pickle.load(f)
f.close()
print(d)

image.png

image.png

# json 模块

json 模块的 dumps()loads() 函数是定义得非常好的接口的典范。

# json.dumps (python 对象) python 对象 -》json 对象

dumps() 方法返回一个 str ,内容就是标准的 JSON

# json.loads (json 对象) json 对象 -》python 对象

json.``dump (obj, fp, , skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan=True, cls=None, indent=None, separators=None, default=None, sort_keys=False, kw)*

# 类变为字典并序列化

json.dumps(s, default=lambda obj: obj.__dict__)

# 进程和线程

Python 的标准库提供了两个模块: _threadthreading_thread 是低级模块, threading 是高级模块

线程是最小的执行单元,而进程由至少一个线程组成

操作系统轮流让各个任务交替执行

真正的并行执行多任务只能在多核 CPU 上实现

对于操作系统来说,一个任务就是一个进程(Process),比如打开一个浏览器就是启动一个浏览器进程

Word,它可以同时进行打字、拼写检查、打印等事情。在一个进程内部,要同时干多件事,就需要同时运行多个 “子任务”,我们把进程内的这些 “子任务” 称为线程(Thread)

  • 多进程模式;
  • 多线程模式;
  • 多进程 + 多线程模式。

# 多进程

Unix/Linux 操作系统提供了一个 fork() 系统调用,普通的函数调用,调用一次,返回一次,但是 fork() 调用一次,返回两次,因为操作系统自动把当前进程(称为父进程)复制了一份(称为子进程),然后,分别在父进程和子进程内返回。

创建子进程

from multiprocessing import Process
import os
def run_pro(name):
    print('开始运行子进程%s,%s'%(name,os.getpid()))
if __name__ == '__main__':
    print('开始运行进程%s' % (os.getpid()))
    p=Process(target=run_pro,args=('test',))
    p.start()
    p.join()
    print('end')

image.png

# 启动大量子进程 pool

进程池

import time, threading
# 新线程执行的代码:
def loop():
    print('thread %s is running...' % threading.current_thread().name)
    n = 0
    while n < 5:
        n = n + 1
        print('thread %s >>> %s' % (threading.current_thread().name, n))
        time.sleep(1)
    print('thread %s ended.' % threading.current_thread().name)
print('thread %s is running...' % threading.current_thread().name)
t = threading.Thread(target=loop, name='LoopThread')
t.start()
t.join()
print('thread %s ended.' % threading.current_thread().name)

# 模块总结

# doctest 文档测试

# os.path 文件路径

# pickle 序列化

# json

使用 dict 和 set - 廖雪峰的官方网站 (liaoxuefeng.com)

用于学习记录,后期便于复习,参考链接

image.png

python file

image.png

image.png

调用脚本时会先载入 pyhton 解释器,然后运行脚本

rpm:软件管理包

操作符优先级:image.png

条件判断:

image.png

Python 为我们提供了非常完善的基础代码库,覆盖了网络、文件、GUI、数据库、文本等大量内容,被形象地称作 “内置电池(batteries included)”。用 Python 开发,许多功能不必从零编写,直接使用现成的即可。

语言定位:

Python 的定位是 “优雅”、“明确”、“简单”

Python 是解释型语言

image.png

# python

image.png

image.pngimage.png

image.png

# 数据类型

# int 整型

long int 长整型

int 整型

# float 浮点型

浮点数也就是小数,之所以称为浮点数,是因为按照科学记数法表示时

双精度浮点型 e

浮点型

# String 字符串

字符串是以单引号 ' 或双引号 " 括起来的任意文本

字符串是以 Unicode 编码

对于单个字符的编码,Python 提供了 ord() 函数获取字符的整数表示, chr() 函数把编码转换为对应的字符:

# Bool 布尔

True

Flase

# None 空值

None, None 不能理解为 0 ,因为 0 是有意义的,

Null 无意义

.

<iframe src="http://127.0.0.1:6806/widgets/brython-editor" data-src="http://127.0.0.1:6806/widgets/brython-editor" data-subtype="widget" border="0" frameborder="no" framespacing="0" allowfullscreen="true" style="width: 1341px; height: 276px;"></iframe>

# 变量

变量的概念基本上和初中代数的方程变量是一致的,

变量不仅可以是数字,还可以是任意数据类型。

变量名必须是大小写英文、数字和 _ 的组合,且不能用数字开头,字母或下划线开头

#变量类型
a=1
b='我是变量'
c=True
d=12.2e3
print(a,b,c,d)
print(type(a),type(b),type(c),type(d))

image.png

int a=1 静态语言 此时已经分配的 int 分区之后不能更改变量类型【不支持,Java】

a=3 动态语言,可以赋值成任意类型

# 动态定义

# 静态定义

a = 'ABC'
b = a
a = 'XYZ'
print(b)

执行 a = 'ABC' ,解释器创建了字符串 'ABC' 和变量 a ,并把 a 指向 'ABC'

py-var-code-1

执行 b = a ,解释器创建了变量 b ,并把 b 指向 a 指向的字符串 'ABC'

py-var-code-2

执行 a = 'XYZ' ,解释器创建了字符串 'XYZ',并把 a 的指向改为 'XYZ' ,但 b 并没有更改:

py-var-code-3

# 常量

所谓常量就是不能变的变量,通常用全部大写的变量名表示常量:

PI = 3.14159265359

# list 列表

list 是一种有序的集合,可以随时添加和删除其中的元素。

list=['a','b','c']
print(list)
print(len(list))

image.pngimage.png

# 切分

image.pngimage.png

# append()追加

str.append('a')

image.pngimage.png

# insert 插入指定位置

image.pngimage.png

# pop()删除末尾元素

要删除指定位置的元素,用 pop(i) 方法,其中 i 是索引位置

替换元素直接赋值即可

列表可以嵌套

s = ['python', 'java', ['asp', 'php'], 'scheme']

类型可以不同

L = ['Apple', 123, True]

# 切片

list [:-1] 不包含最后一个元素

list [:] 全部列表

list [::] 全部列表

image.pngimage.png

前 10 个数,每两个取一个

image.pngimage.png

# 列表生成

list(range(1,11))生成 10 个数 1-10

print([m+n for m in '123' for n in 'yza'])
k=[1,1,23,45,56,[1,12,3,467,[2,4,4,3,22]]]
print([x for x in k if x==1 ])

image.png

# tuple 元组

tuple 一旦初始化就不能修改

image.pngimage.png

但元组初始化后就不能进行更改了

b=(1)
#定义的不是 tuple,是 1 这个数!
# 这是因为括号 () 既可以表示 tuple,又可以表示数学公式中的小括号,
# 这就产生了歧义,因此,Python 规定,
# 这种情况下,按小括号进行计算,计算结果自然是 1
print(b)
print(type(b))
c=(1,)
print(c)
print(type(c))

image.png

image.png

# dict 字典

其他语言叫 map,使用键 - 值(key-value)存储,具有极快的查找速度。dict 的 key 必须是不可变对象。key 计算位置的算法称为哈希算法(Hash)。

# 定义

d={'name':'ruanyifen','age':60,'happy':'write'}
d['add']='Im add'
d['name']='fix'

image.png

# 取 value

# dict['key']

# 'key' in dict

# dict.get('key')

print(type(d))
print(d['name'])
print('name' in d)#方法一判断是否有这个主键在字典 d 中
print(d.get('name'))#方法二 取

image.png

# dict.pop ('key') 删除一个 key

image.png

# dict.keys 返回字典中所有 key 列表

# dict.update () 将 a 字典新 key,value 内容加入 b 字典中

dicta={"name":'ruan','age':20}
dictb={"name":'ruan2','age':40,'add':'w shi add'}
dictb.update(dicta)
print(dictb)

image.png

image.png

# 内建函数使用

type()

cmp()

len()

hash()

image.png

内建 cmp()函数比较两个 dict 时,先比较长度,后比值,输出 1 或 - 1

image.png

image.png

# dict 特点

dict 有以下几个特点:

  1. 查找和插入的速度极快,不会随着 key 的增加而变慢;
  2. 需要占用大量的内存,内存浪费多。

而 list 相反:

  1. 查找和插入的时间随着元素的增加而增加;
  2. 占用空间小,浪费内存很少。
    image.png
    image.png

# set 集合

也是一组 key 的集合,但不存储 value。由于 key 不能重复,所以,在 set 中,没有重复的 key。

重复元素在 set 中自动被过滤

# 定义

image.pngimage.png

image.pngimage.png

# set.add ('key') 添加元素

但重复元素不添加,自动去重

image.pngimage.png

# set.remove ('key') 删除元素

image.pngimage.png

set 可以看成数学意义上的无序和无重复元素的集合,

因此,两个 set 可以做数学意义上的交集、并集等操作:

# & 两个 set 交集

image.pngimage.pngimage.png

# | 两个 set 并集

image.pngimage.pngimage.png

image.png

# map () 的显示

打印 map 对象可以看到 map 对象返回的是一个地址,不是真实的数据

print(list(map对象))
print([it for it in map对象])

# 数据类型转换

# int()

# float()

# str()

# bool()

image.pngimage.png

# 条件判断

# if

if else

a=100
if a>=0:
    print(a)
else:
    print(-a)

image.png

if

if True:
    print('True')

image.png

if elif elif else

name='zhangsan'
if name=='zhangsan':
    print('我是',name)
elif name=='lisi':
    print('我是', name)
elif name=='wangwu':
    print('我是', name)
else:
    print('我谁的不是')

image.png

# input()输入输出

input () 返回的数据类型是 str

print()

# 循环 迭代

list,tuple,dict 都可循环

Python 的 for 循环本质上就是通过不断调用 next() 函数实现的,计算是惰性的

dict 循环按照 value 时:for value in dict.values

for value in d.values():
    print(value)

# for in

sum=0
for i in range(1,100):
    sum=sum+i
print(sum)

image.pngimage.pngimage.png

# while

sum2=0
k=0
while(k<100):
    sum2=sum2+k
    k=k+1
print(sum2)

image.png

# break

如果要提前结束循环,可以用 break 语句

# continue

通过 continue 语句,跳过当前的这次循环,直接开始下一次循环

# 生成器

在 Python 中,这种一边循环一边计算的机制,称为生成器:generator。

包括生成器和带 yield 的 generator function。

g = (x * x for x in range(10))

image.png

访问大文件

yield

# isinstance()迭代器

直接作用于 for 循环的对象统称为可迭代对象,都是迭代器 Iterable

listtupledictsetstr

listdictstr 虽然是 Iterable ,却不是 Iterator

listdictstrIterable 变成 Iterator 可以使用 **iter ()** 函数

以直接作用于 for 循环的数据类型有以下几种:

一类是集合数据类型,如 listtupledictsetstr 等;

一类是 generator ,包括生成器和带 yield 的 generator function。

可以使用 **isinstance ()** 判断一个对象是否是 Iterable 对象__iter__:

迭代对象

判断是不是可以迭代,用 Iterable

from collections import Iterable
isinstance({}, Iterable) --> True
isinstance((), Iterable) --> True
isinstance(100, Iterable) --> False

判断是不是迭代器,用 Iterator

from collections import Iterator
isinstance({}, Iterator)  --> False
isinstance((), Iterator) --> False
isinstance( (x for x in range(10)), Iterator)  --> True

Python 中 list,truple,str,dict 这些都可以被迭代,但他们并不是迭代器,为什么::因为和迭代器相比有一个很大的不同,list/truple/map/dict 这些数据的大小是确定的,也就是说有多少事可知的。但迭代器不是,迭代器不知道要执行多少次,所以可以理解为不知道有多少个元素,每调用一次 next (),就会往下走一步,是惰性的。

# 函数

抽象

将函数抽象成一个函数名称,不看内部结构直接调用方法

返回类型 函数名(输入参数):

函数体

# 调用函数

要调用一个函数,需要知道函数的名称和参数

绝对值 abs

image.pngimage.png

# 定义函数

def myabs(x):
    if x>0:
        return x
    if x<0:
        return -x
print(myabs(-100))

image.png

# 空函数

def nufun():
    pass

pass 可以用来作为占位符

# 函数 参数检查

def my_init_abs(x):
    if not isinstance(x,(int,float)):
        raise TypeError('no no no')
    else:
        if x>0:
            print(x)
        if x<0:
            print(-x)
my_init_abs(-90)

image.png

# 可返回多个值,函数

def return_much():
    a='返回'
    b='我也返回'
    c='我也要返回'
    return a,b,c
print(return_much())
print(type(return_much()))

image.png

# 函数参数

*args 是可变参数,args 接收的是一个 tuple;

**kw 是关键字参数,kw 接收的是一个 dict

power(x) 函数,参数 x 就是一个位置参数,可单个变量,list,set,tuple

power(*x) 函数,可传入单个变量,list,set,tuple,可以传入任意个参数或 0 个参数

power(**kw) 函数,字典 dict

可变参数允许你传入 0 个或任意个参数,这些可变参数在函数调用时自动组装为一个 tuple。

而关键字参数允许你传入 0 个或任意个含参数名的参数,这些关键字参数在函数内部自动组装为一个 dict.

power(x, n) ,用来计算 xn

power(x, n) 函数有两个参数: xn

默认参数,此时 age 和 city 为默认参数,可传值改变也可不变【不用传值】

power(L=None) 函数有 None 这个不变对象,可用 list

def enroll(name, gender, age=6, city='Beijing'):
    print('name:', name)
    print('gender:', gender)
    print('age:', age)
    print('city:', city)

image.png

# 必选参数

def a1(x):
    return x
print(a1(2))
print(a1(12.1))
print(a1('ruan'))
print(a1(True))
print(a1([1,2,3]))
print(a1({1,2,3}))
print(a1({"key":"vleaue",'name':'ruan','mun':23}))

image.png

# = 默认参数

def a2(x=9):
    return x
print(a2())
print(a2(2))
print(a2(12.1))
print(a2('ruan'))
print(a2(True))
print(a2([1,2,3]))
print(a2({1,2,3}))
print(a2({"key":"vleaue",'name':'ruan','mun':23}))

image.png

# * 可变参数

def a3(*x):
    return x
print(a3())
print(a3(2))
print(a3(12.1))
print(a3('ruan'))
print(a3(True))
print(a3([1,2,3]))
print(a3({1,2,3}))
print(a3({"key":"vleaue",'name':'ruan','mun':23}))

image.png

# ** 关键字参数

def a3(**kw):
    return kw
print(a3())
print(a3(kw=2))
print(a3(kw=12.1))
print(a3(kw='ruan'))
print(a3(kw=True))
print(a3(kw=[1,2,3]))
print(type(a3(kw=[1,2,3])))
print(a3(kw={1,2,3}))
print(type(a3(kw={1,2,3})))
print(a3(kw={"key":"vleaue",'name':'ruan','mun':23}))

image.png

# 递归函数

在函数内部,可以调用其他函数。

一个函数在内部调用自身本身,这个函数就是递归函数

使用递归函数需要注意防止栈溢出

#递归函数
def funmyself(x):
    if x>1:
        return x+funmyself(x-1)
    elif x==1:
        return 1
print(funmyself(3))

image.png

解决栈溢出方法:

尾递归优化,事实上尾递归和循环的效果是一样的

尾递归是指,在函数返回的时候,调用自身本身,并且,return 语句不能包含表达式

#尾递归
def funmyself2(n):
    return  funmyself2_it(n,1)
def  funmyself2_it(n,pro):
    if n==1:
        return pro
    else:
        return funmyself2(n-1)+n
print(funmyself2(100))

image.png

此时 funmyself2 是尾递归函数

# 转义字符 \

转义字符 \ 可以转义很多字符,比如

\n 表示换行,

\t 表示制表符,

字符 \ 本身也要转义,所以 \\ 表示的字符就是 \

Python 还允许用 r'' 表示 '' 内部的字符串默认不转义

# 运算符 and、or 和 not

运算优先级:not>or>and

# 除法 ///

print(10/3)
print(10//3)

# 除法一 / 浮点数

# 除法二 // 地板除 整数

/ 除法计算结果是浮点数,即使是两个整数恰好整除,结果也是浮点数:

// ,称为地板除,两个整数的除法仍然是整数:

# 取余 %

print(10%1)
print(10%3)
print(4%7)
print(2%20)
#如果 a% b a>b 则结果为 a

image.png

# 字符编码

ASCII 编码

8 个比特(bit)作为一个字节(byte)

一个字节能表示的最大的整数就是 255(二进制 11111111 = 十进制 255)

两个字节可以表示的最大整数是 65535 ,4 个字节可以表示的最大整数是 4294967295

大写字母 A 的编码是 65 ,二进制的 01000001 ,小写字母 z 的编码是 122

Unicode 把所有语言都统一到一套编码里,这样就不会再有乱码问题

ASCII 编码是 1 个字节,而 Unicode 编码通常是 2 个字节

ASCll 出现乱码问题引入 Unicode 编码存储空间多了一倍引入 UTF-8 编码

utf-8:将 Unicode 字符根据不同的数字大小编码成 1-6 个字节,常用的英文字母被编码成 1 个字节,汉字通常是 3 个字节,

字符ASCIIUnicodeUTF-8
A0100000100000000 0100000101000001
x01001110 0010110111100100 10111000 10101101

在计算机内存中,统一使用 Unicode 编码,当需要保存到硬盘或者需要传输的时候,就转换为 UTF-8 编码。

用记事本编辑的时候,从文件读取的 UTF-8 字符被转换为 Unicode 字符到内存里,编辑完成后,保存的时候再把 Unicode 转换为 UTF-8 保存到文件

image.png

浏览网页的时候,服务器会把动态生成的 Unicode 内容转换为 UTF-8 再传输到浏览器:

web-utf-8

# compile () 字符串编译为字节代码

# 编码转化

# ord ('A') 字母转字符

# chr (65) 字符转字母

a=ord('A')
b=chr(65)
print(a,b)

image.png

# b'str' 转为字节类型 bytes

bytes 类型的数据用带 b 前缀的单引号或双引号表示

x = b'ABC'

要注意区分 'ABC'b'ABC' ,前者是 str ,后者虽然内容显示得和前者一样,但 bytes 的每个字符都只占用一个字节。

image.pngimage.png

# str.encode('ascii') str 变为 bytes

ASCII

UTF-8

image.pngimage.png

# str.decode('utf-8') bytes 变为 str

print(type(b'abc'))
print(b'abc'.decode('utf-8'))
print(type(b'abc'.decode('utf-8')))

image.pngimage.png

len(str)计算字符数

函数计算的是 str 的字符数,如果换成 byteslen() 函数就计算字节数

print(len('abc'))
print(len(b'abc'))
print(len('中'))
print(len('中'.encode('utf-8')))

image.pngimage.png

# 特殊注释

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

第一行注释是为了告诉 Linux/OS X 系统,这是一个 Python 可执行程序,Windows 系统会忽略这个注释

第二行注释是为了告诉 Python 解释器,按照 UTF-8 编码读取源代码,否则,你在源代码中写的中文输出可能会有乱码。

# 占位符 格式化

# 占位符 % s % d % f

image.png

格式化方式和 C 语言是一致

% 运算符就是用来格式化字符串的。

在字符串内部,

%s 表示用字符串替换,

%d 表示用整数替换,

有几个 %? 占位符,后面就跟几个变量或者值,顺序要对应好。如果只有一个 %? ,括号可以省略

image.pngimage.png

占位符替换内容
%d整数
%f浮点数
%s字符串
%x十六进制整数

image.pngimage.png

转义: %% 来表示一个 %

# format()格式化字符串

image.png

image.png

# f-string 格式化字符串

{r} 被变量 r 的值替换, {s:.2f} 被变量 s 的值替换,并且 : 后面的 .2f 指定了格式化参数(即保留两位小数),因此, {s:.2f} 的替换结果是 19.62

image.pngimage.png

# 函数式编程

函数是 Python 内建支持的一种封装,通过层层函数进行调用

#面向过程的程序设计 #:把复杂任务分解成简单的任务,这种分解可以称之为面向过程的程序设计

函数式和函数的区别:

对比例子:计算和计算器的区别

编程语言,就是越低级的语言,越贴近计算机,抽象程度低,执行效率高,比如 C 语言;越高级的语言,越贴近计算,抽象程度高,执行效率低,比如 Lisp 语言

Python 不是纯函数式编程语言

函数式编程就是一种抽象程度很高的编程范式,

纯粹的函数式编程语言编写的函数没有变量,因此,任意一个函数,只要输入是确定的,输出就是确定的

# 函数式编程特点:

  1. 纯函数式编程语言函数没有变量,输入输出确定
  2. 允许本身作为参数传入另一个函数,允许返回一个函数

# 高阶函数

参数中有函数

高阶函数除了可以接受函数作为参数外,还可以把函数作为结果值返回

# 变量可以指向函数

a = 函数

求绝对值的函数 abs() 为例

print(abs(-10))
print(abs)

image.png

abs(-10)是函数调用,abs 是函数本身

k=abs(-20)
print(k)
#函数本身也可以赋值给变量
h=abs
print(h)
print(h(-100))

image.png

结论:函数本身也可以赋值给变量,即:# 变量可以指向函数。#

# 函数名也是变量

#函数名 #:其实就是指向函数的变量

a () 中 a 是指向函数 a()的变量

# 传入函数

既然变量可以指向函数,函数的参数能接收变量,那么一个函数就可以接收另一个函数作为参数,这种函数就称之为高阶函数。

#高阶函数 #:一个函数就可以接收另一个函数作为参数

b()

a(b)

x=a

def f(x):
    return abs(int(x))
def a(a,b,f):
    return f(a)+f(b)
if __name__ == '__main__':
    a1=input('a')
    a2=input('b')
    print(a(a1,a2,f))

image.png

此时函数 a 为高阶函数,需要调用 f 函数作为参数

# map/reduce 内建函数

内建了 map()reduce() 函数 高阶函数

# map()函数处理生成新 Iterator 迭代器

image.png 两个参数,函数名【函数本身】,需要处理的编程式 iterator

<br /> 创建一个迭代器,使用每个迭代器中的参数计算函数。当最短迭代用尽时停止。

map(func, *iterables) --> map object
def f(x):
    return x*x
r=map(f,[1,2,3,4,4,4,4,4,4,4,4])
print(r)
print(type(r))
print(list(r))
print(type(list(r)))

image.png

运算规则抽象

# reduce()函数作用在序列上

image.png

两个参数,函数名【函数本身】,需要处理的 #序列 #: sequence (序列) 是一组有顺序的元素的集合

序列基本样式 [下限:上限:步长]

reduce 把结果继续和序列的下一个元素做累积计算

reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)
from functools import reduce
>>> def fn(x, y):
...     return x * 10 + y
...
>>> reduce(fn, [1, 3, 5, 7, 9])
13579

image.png

# filter () 过滤序列

参数和 map()相似

filter() 也接收一个函数和一个序列

# sorted()排序

高阶函数image.png

参数:排序对象,key = 函数

sorted([36, 5, -12, 9, -21], key=abs)

排序的核心是比较两个元素的大小

print(sorted([1,2,353,6,3,234,43,435]))

image.png

key 指定绝对值大小排序

print(sorted([1,2,353,6,3,234,43,435,-242,-34,34,35],key=abs))

image.png

# 返回函数

# 函数作为返回值

高阶函数除了可以接受函数作为参数外,还可以把函数作为结果值返回

# 如果不需要立刻求和,而是在后面的代码中,
# 根据需要再计算怎么办?可以不返回求和的结果,而是返回求和的函数:
def zary_sum(a):
    def sum():
        sum1=0
        for i in a:
            sum1=sum1+i
        return sum1
    return sum
print(type(zary_sum([1,2,3,4])))
f=zary_sum([1,2,3,4])
print(f())

image.png

调用返回函数时,每次调用都会新生成一个函数

image.png

# 闭包

当一个函数的返回值是另外一个函数,

而返回的那个函数如果调用了其父函数内部的其它变量,如果 返回的这个函数在外部被执行,就产生了闭包

返回函数中,返回的函数调用父函数的内部变量

image.png

#返回函数
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(),f2(),f3())

image.png

返回一个函数时,牢记该函数并未执行,返回函数中不要引用任何可能会变化的变量。

# lambda()匿名函数

lambda 关键字 函数参数:函数表达式

传入函数时,有些时候,不需要显式地定义函数

Python 对匿名函数的支持有限,只有一些简单的情况下可以使用匿名函数。

lambda x:x*x
#等价于
def f(x):
   return x*x

关键字 lambda 表示匿名函数,冒号前面的 x 表示函数参数,只能一个表达式

不用写 return ,返回值就是该表达式的结果。

匿名函数也是一个函数对象

f=lamdba x:x*x

判断奇数函数

原函数:

def is_odd(n):
    return n % 2 == 1
L = list(filter(is_odd, range(1, 20)))

采用匿名函数修改

l=list(filter(lambda x:x%2==1,range(1,20)))
print(l)

image.png

# 装饰器 Decorator

# 本质上,装饰器就是一个返回函数的高阶函数

@log 等价于

now = log(now)

由于函数也是一个对象,而且函数对象可以被赋值给变量,

所以,通过变量也能调用该函数

image.pngimage.png

def log(func):
    def wrapper(*args,**kwargs):
        print('call %s'% func.__name__)
        return func(*args,**kwargs)
    return wrapper()
#func 为参数所以是高阶函数
#return 函数所以是返回函数,
#没有调用父函数中参数,所以不是闭包

image.pngimage.png

场景注意:

无 @装饰器时函数不调用,需要参数才调用

当 @时会直接调用装饰器定义函数然后执行函数,不用调用函数

三层时,传入参数

def log1(text):
    def decorator(func):
        def wapper(*args,**kw):
            print('%s %s'%(text,func.__name__))
            return func(*args,**kw)
        return wapper
    return decorator
@log1('ruan')
def now3():
    print("hhh")
now3()

相当于在返回高阶函数上还有一个函数,所以返回时应该还要调用一次

# @wraps 常用装饰器

当装饰器是个闭包时,装饰器调用变量会改变增加 @wraps 后装饰器内的变量不变

装饰器在装饰一个函数时,,原函数就成了一个新的函数,也

就是说其属性会发生变化,所以为了 不改变原函数的属性

我们会调用 functools 中的 wraps 装饰器来保证原函数的属性不变

# 不加 wraps 时

@wraps(func)
from functools import wraps
def wrap(func):
   
    def b():
        'b'
        print('decorator:',b.__name__)
        print('funname',func.__name__)
        func()
    return b
@wrap
def a():
    'a'
    print('name',a.__name__)
a()

image.png

加装饰器 wraps 时

from functools import wraps
def wrap(func):
    @wraps(func)
    def b():
        'b'
        print('decorator:',b.__name__)
        print('funname',func.__name__)
        func()
    return b
@wrap
def a():
    'a'
    print('name',a.__name__)
a()

image.png

闭包的概念:调用父函数中的变量的函数,为了保证数据安全。变量作用域只在函数内部,可在闭包中操作数据。

装饰器返回为什么是函数名(函数内存地址)而不直接执行函数?

当有参数传入时,可直接与调用的函数中的值传入参数执行。

()是运算符 f () 与 f.call () 等价:将 f 对象变成变成可调用的对象

# 偏函数(functools 模块)

属于 functools 模块

# 作用:

通过设定参数的默认值,降低函数调用的参数

int() 函数默认按十进制转换

print(int('100',base=8))

经常调用于是重写一个函数 int2

def int2(x, base=8):
    print(int(x, base))
    return int(x, base)
print(int2('2334'))

采用偏函数

import functools
int3=functools.partial(int,base=8)
print(int3('46'))
print(int())

image.png

functools.partial 的作用是将函数的特定参数固定住(设定为默认值)

image.png

创建偏函数的时候也可以接收,函数对象,*args,**kw

# 模块

python 包:作用区分相同名称的模块

模块相当于一个 py 文件

image.png

image.png

# 作用域

仅仅在模块内部使用。在 Python 中,是通过 _ 前缀来实现的。

# pubilc 公开

正常的函数和变量名是公开的(public)

# private 非公开_,__

_xxx 和__xxx 这样的函数或变量就是非公开的(private)

# 安装第三方模块 pip

pip install 模块名

# 模块搜索路径

import sys
print(sys.path)

两种方式:

  1. 添加搜索路径
    import sys
    sys.path.append('/Users/michael/my_py_scripts')
  2. 设置环境变量

第二种方法是设置环境变量 PYTHONPATH

# 面向对象编程

面向对象编程 ——Object Oriented Programming,简称 OOP,是一种程序设计思想

对象作为程序的基本单元,

一个对象包含了数据和操作数据的函数

数据封装、继承和多态是面向对象的三大特点

# 类和实例

面向对象最重要的概念就是类(Class)和实例(Instance)

类是抽象出来的模板

实例是根据类创建出的对象,每个对象可能有属性和方法

定义类是通过 class 关键字,类名通常是大写开头的单词

class Student(object):
    pass

image.png

!!!在类中定义函数有一点不同,定义佛如方法第一个参数永远是实例变量本身 self

仍然可以用默认参数、可变参数、关键字参数和命名关键字参数

# 数据封装

class Student(object):
    def __init__(self, name, score):
        self.name = name
        self.score = score
    def get_grade(self):
        if self.score >= 90:
            return 'A'
        elif self.score >= 60:
            return 'B'
        else:
            return 'C'

image.png

# 访问限制

# 作用:

确保了外部代码不能随意修改对象内部的状态

实例的变量名如果以 __ 开头,就变成了一个私有变量(private)

外部无法访问_name

class Student(object):
    def __init__(self,name,age):
        self._name=name
        self.age=age
    def print_name(self):
        print(self._name)
        return self.age,self._name
a=Student('ruan',23)
h=a.print_name()
print(h)

image.png

若是要获取,修改变量增加 get,set 方式即可

class Student(object):
    def __init__(self,name,age):
        self._name=name
        self.age=age 
    def get_name(self):
        return self.name
    def set_name(self,name):
        self._name=name

Python 本身没有任何机制阻止你干坏事,一切全靠自觉。

类外部无法访问

# 继承和多态

# 继承

# 多态

在继承关系中,如果一个实例的数据类型是某个子类,那它的数据类型也可以被看做是父类。

比如:动物是父类,狗和鱼是子类;鱼是鱼类,鱼是动物都成立。

判断一个变量是否是某个类型可以用 isinstance() 判断

# 鸭子类型

并不要求严格的继承体,一个对象只要 “看起来像鸭子,走起路来像鸭子”,那它就可以被看做是鸭子。

# 获取对象信息

# type()判断对象类型

image.png

# isinstance () 对于继承关系,判断 class 的类型

# dir()获取对象的所有属性和方法

# len()对象长度

# lower()返回小写的字符串

# getattr()获取属性 a

# setattr()设置属性 a

# hasattr(obj,'a')判断是否有属性 a

getattr(obj, 'z', 404) # 获取属性 'z',如果不存在,返回默认值 404
404

# 实例属性和类属性

image.png

class Student(object):
    name='ruan'
   
h=Student()
h.name='hhh'

类中的 name 是类属性,

创建 h 类对象即实例后赋值的是实例属性 name,但由于实例对象的优先级比类属性高,会屏蔽类中的 name 属性,即 h.name 的值为 hhh

# 总结:

  1. 实例属性属于各个实例所有,互不干扰;
  2. 类属性属于类所有,所有实例共享一个属性;
  3. 不要对实例属性和类属性使用相同的名字,否则将产生难以发现的错误

# 面向对象高级编程

数据封装、继承和多态只是面向对象程序设计中最基础的 3 个概念

多重继承、定制类、元类

# _slots_使用

可以给创建的实例绑定属性和方法

给一个实例绑定的方法对另外一个实例对象是不起作用的

class A:
    def run(self):
        print("i im ferther runing....")
sun1=A()
#给实例 sun1 设置 name 属性
sun1.name='i im name'
#创建实例对象 2
sun2=A()
#实例对象 sun1 的属性和 sun2 无关,即 sun2 没有 name 属性
#给实例 sun1 绑定方法,方法和属性同理
#定义方法
def setAll(self,num):
    print(num)
sun1.newfun=MethodType(setAll, sun1)
sun1.newfun(37)
#若所有实例都需要绑定方法则给类绑定方法
A.setAll=setAll
#给类绑定方法后,所有创建的实例的均可调用
def set_age(self, age): # 定义一个函数作为实例方法
...     self.age = age
...
>>> from types import MethodType
>>> s.set_age = MethodType(set_age, s) # 给实例绑定一个方法
>>> s.set_age(25) # 调用实例方法

# 限制实例属性,定义一个特殊的 __slots__ 变量

class Student(object):
    __slots__ = ('name', 'age') # 用 tuple 定义允许绑定的属性名称
s=Student()
s.name='ruan'
s.firstname='i im firstname'
#输出的时候 firstname 的属性会报错,
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Student' object has no attribute 'firstaname'

# 注意:

_slots_使用时要注意,定义的属性只在当前的类的实例中,对于继承的子类是不起作用的

class People(object):
    __slots__ = ('name','age')
    def run(self):
        print('i im run people......')
class Teacher(People):
    def say(self):
        print('i im teacher....')
t=Teacher()
t.tall='shouhua'
print(t.tall)
p=People()
p.tall('shouhuap')
print(p.tall)

image.png

只限制父类 People 的属性,而子类 Teacher 中不限制

# @property

在绑定属性时,如果我们直接把属性暴露出去,导致可以随意更改。通过 get,set 来获取更改属性值。

在 python 中直接调用装饰器将一个方法变成属性调用

class Student(object):
    @property
    #使用 get 方法是调用装饰器 @peoperty,
    # 同时自动创建了另一个装饰器 @属性.setter
    def score(self):
        return self.score
    @score.setter
    def score(self,value):
        self._score=value

# 总结:

- 权限限制只对类对象实际起作用,想要达到方法和属性强制访问权限,需要使用 @property 装饰器进行 get,set 方法

属性名与方法名一定要区分开,不然会进入死循环(self._age,def age ())
实例化的对象使用属性时,不是调用属性(meizi._age),而是用的方法名(meizi.age)
@property 其实就是实现了 getter 功能; @xxx.setter 实现的是 setter 功能;还有一个 @xxx.deleter 实现删除功能
定义方法的时候 @property 必须在 @xxx.setter 之前,且二者修饰的方法名相同(age ())
如果只实现了 @property(而没有实现 @xxx.setter),那么该属性为 只读属性

#请利用 @property 给一个 Screen 对象加上 width 和 height 属性,
# 以及一个只读属性 resolution:
class Screen(object):
    __slots__ = ('_width','_height','_resolution')
    @property
    def width(self):
        return self._width
    # 方法名称和实例变量均为 width:
    @width.setter
    def width(self,widthValue):
        self._width=widthValue
    @property
    def height(self):
        return self._height
    @width.setter
    def height(self, height):
        self._height = height
    @property
    def resolution(self):
        return self._width * self._height
s=Screen()
s.width=23
s.height=12
print(s.resolution)

image.png

s.score = 60 # OK,实际转化为 s.set_score (60)
s.score # OK,实际转化为 s.get_score ()

要特别注意:属性的方法名不要和实例变量重名。例如,以下的代码是错误的:

class Student(object):

    # 方法名称和实例变量均为birth:
    @property
    def birth(self):
        return self.birth

出现递归调用错误

image.png

之前的例子中 width 和_width 不同所以可以运行

# 多重继承

image.png

python 可以支持多继承,即一个子类可以继承多个父类;但 java 是单继承,只能有一个父类

Tercher(Name,study,teach)即 Teacher 可以继承多个父类

# MixIn

在设计类的继承关系时,通常,主线都是单一继承下来的,例如, Teacher 继承自 Name。但是,如果需要 “混入” 额外的功能,通过多重继承就可以实现,比如 Teacher 除了继承自 Name 外,再同时继承 Teach 。这种设计通常称之为 MixIn

Python 自带了 TCPServerUDPServer 这两类网络服务,而要同时服务多个用户就必须使用多进程或多线程模型,这两种模型由 ForkingMixInThreadingMixIn 提供。

# 多继承

多重继承这个名词一般用来形容继承链条可以很长,多个层次。

# 多重继承

多继承则指一个类可以有多个基类,相反则是单继承。任何面向对象编程语言都支持多重继承,但像 java 这种只能通过接口实现有限程度的多继承

问:多继承 如果多个类有共同得方法名 怎么区分是调得哪个类🤡

答:调用该方法的时候,会调用第一顺位继承父类的方法

# 总结:

  1. Python 允许使用多重继承,因此,MixIn 就是一种常见的设计
  2. 只允许单一继承的语言(如 Java)不能使用 MixIn 的设计

# 定制类

Python 的 class 中还有__xxx__有特殊用途的函数,可以帮助我们定制类

# str () 回用户看到的字符串

将对象 <__main__.Student object at 0x109afb190> 变成易读的数据

只在调用 print 时会调用__str__,交互界面时还是现实上方不易读的对象内容,此时用

# repr () 返回程序开发者看到的字符串

__str__() 返回用户看到的字符串,而 __repr__() 返回程序开发者看到的字符串,

也就是说, __repr__() 是为调试服务的

简写

def __str__(self):
        return 'xxx object (name=%s)' % self.name
__repr__ = __str__

# _iter () 返回一个迭代对象

需要用到 for in 迭代,需要转化为迭代对象

该方法返回一个迭代对象,然后,Python 的 for 循环就会不断调用该迭代对象的 __next__() 方法拿到循环的下一个值,直到遇到 StopIteration 错误时退出循环

例子:

class Fib(object):
    def __init__(self):
        self.a,self.b=0,1
    def __iter__(self):
        return self
    def __next__(self):
        self.a,self.b=self.b,self.a+self.b
        if self.a>1000:
            raise  StopIteration
        return self.a
a=Fib()
for i in a:
    print(i)

image.png

# getitem () 表现得像 list 那样按照下标取出元素

class Fib(object):
    def __init__(self):
        self.a,self.b=0,1
    def __iter__(self):
        return self
    def __next__(self):
        self.a,self.b=self.b,self.a+self.b
        if self.a>1000:
            raise  StopIteration
        return self.a
    def __getitem__(self, item):
        a,b=1,1
        for i in range(item):
            a,b=b,a+b
        return a
a=Fib()
print(a[3])

image.png

以上是传入 int,切片功能实现,isinstance 判断类型

class Fib(object):
    def __init__(self):
        self.a,self.b=0,1
    def __iter__(self):
        return self
    def __next__(self):
        self.a,self.b=self.b,self.a+self.b
        if self.a>1000:
            raise  StopIteration
        return self.a
    def __getitem__(self, item):
        if isinstance(item,int):
            a, b = 1, 1
            for i in range(item):
                a, b = b, a + b
            return a
        if isinstance(item,slice):
            start=item.start
            stop=item.stop
            if start is None:
                start=0
            a,b=1,1
            L=[]
            for x in range(stop):
                L.append(a)
                a,b=b,a+b
        return L
a=Fib()
print(a[3:12])

image.png

# getattr () 动态返回一个属性

调用类属性或方法时,先在__init__() 获取后,再从__getattr__() 获取,获取不到才报错

# call () 直接调用实例本身

与直接调用这个函数一样

class People(object):
    def __init__(self,name):
        self.name=name
    def __call__(self, *args, **kwargs):
        print('i im call %s'% self.name)
p=People('ruan')
p()

image.png

# 使用枚举类

枚举类:在某些情况下,一个类的 实例对象 的数量有限且固定 的,如季节类,它的实例对象只有春、夏、秋、冬。 在 Java 中像这种对象实例有限且固定的类被称为枚举类;这样的枚举类型定义一个 class 类型,然后,每个常量都是 class 的一个唯一实例。Python 提供了 Enum 类来实现这个功能。

from enum import Enum
M=Enum('a',('sun1','sun2','sun3','sun4'))
print(M.sun1)

image.png

自定义枚举类

from enum import Enum,unique
@unique
class Week(Enum):
    sun1=1
    sun2=2
    sun3=3
day2=Week.sun2
print(day2)

image.png

from enum import Enum,unique
@unique
class Gender(Enum):
    Male=0
    Female=1
class Student(object):
    def __init__(self,name,gender):
        self.name=name
        self.gender=gender
# 测试:
bart = Student('Bart', Gender.Male)
if bart.gender == Gender.Male:
    print('测试通过!')
else:
    print('测试失败!')

image.png

# 使用元类 [创建类]

实例对象是类创建

类是元类创建

创建类的方式

# 方式一:type()

type() 函数既可以返回一个对象的类型,又可以创建出新的类型,比如,我们可以通过 type() 函数创建出 Hello

from class1104 import *
h=Hello()
print(type(h))
print(type(Hello))

image.png

Hello = type('Hello', (object,), dict(hello=fn))

要创建一个 class 对象, type() 函数依次传入 3 个参数:

  1. class 的名称;
  2. 继承的父类集合,注意 Python 支持多重继承,如果只有一个父类,别忘了 tuple 的单元素写法;
  3. class 的方法名称与函数绑定,这里我们把函数 fn 绑定到方法名 hello

# 方式二:元类 metaclass

先定义 metaclass,然后创建类。

先定义类,然后创建实例。

metaclass 是 Python 面向对象里最难理解,也是最难使用的魔术代码。

按照默认习惯,metaclass 的类名总是以 Metaclass 结尾,以便清楚地表示这是一个 metaclass

# metaclass 采用 type 创建类 ,metaclass 是类的模板,所以必须从 `type` 类型派生
class ListMetaclass(type):
    def __new__(cls, name,bases,attrs):
        attrs['add']=lambda self, value:self.append(value)
        return type.__new__(cls,name,bases,attrs)
class MyList(list,metaclass=ListMetaclass):
    pass
mylist=MyList()
mylist.add(1)
print(mylist)

image.png

__new__() 方法接收到的参数依次是:

  1. 当前准备创建的类的对象;
  2. 类的名字;
  3. 类继承的父类集合;
  4. 类的方法集合

# 应用场景

ORM 全称 “Object Relational Mapping”,即对象 - 关系映射,就是把关系数据库的一行映射为一个对象,也就是一个类对应一个表,这样,写代码更简单,不用直接操作 SQL 语句。

要编写一个 ORM 框架,所有的类都只能动态定义,因为只有使用者才能根据表的结构定义出对应的类来。

# 错误处理 try

try:
    print('try...')
    r = 10 / 0
    print('result:', r)
except ValueError as e:
    print('ValueError:', e)
except ZeroDivisionError as e:
    print('ZeroDivisionError:', e)e)
finally:
    print('finally...')
print('END')

Python 的错误其实也是 class,所有的错误类型都继承自 BaseException

UnicodeErrorValueError 的子类🤡

Built-in Exceptions — Python 3.10.0 documentation

# 调用栈

让 Python 解释器来打印出错误堆栈

image.png

# 记录错误 logging

可将 logging 生成一个 txt 方便查看

try:
        xxx
    except Exception as e:
        logging.exception(e)

# 抛出错误 raise

except ValueError as e:
        print('ValueError!')
        raise

bar() 函数中,我们明明已经捕获了错误,但是,打印一个 ValueError! 后,又把错误通过 raise 语句抛出去了,这不有病么?

其实这种错误处理方式不但没病,而且相当常见。捕获错误目的只是记录一下,便于后续追踪。但是,由于当前函数不知道应该怎么处理该错误,所以,最恰当的方式是继续往上抛,让顶层调用者去处理。

# 调试方法

# 1. print()

# 2. 断言 assert

assert n != 0, 'n is zero!'

assert 的意思是,表达式 n != 0 应该是 True ,否则,根据程序运行的逻辑,后面的代码肯定会出错。

采用断言的好处:

启动 Python 解释器时可以用 -O 参数来关闭 assert

$ python -O err.py

image.png

关闭后,你可以把所有的 assert 语句当成 pass 来看

# 3. logging

import logging
s = '0'
n = int(s)
logging.info('n = %d' % n)
print(10 / n)

# 4.pbd 单步执行

启动 Python 的调试器 pdb,让程序以单步方式运行,可以随时查看运行状态。

python -m pdb xxx.py
(Pbd) 1#查看第一行代码,单步执行第一行代码

# 5. pdb.set_trace()

这个方法也是用 pdb,但是不需要单步执行,我们只需要 import pdb ,然后,在可能出错的地方放一个 pdb.set_trace() ,就可以设置一个断点:

import pdb
s = '0'
n = int(s)
pdb.set_trace() # 运行到这里会自动暂停
print(10 / n)

可以用命令 p 查看变量,或者用命令 c 继续运行:

image.png

# 6.IDE 工具

vscode,pycharm....

# 单元测试

单元测试是用来对一个模块、一个函数或者一个类来进行正确性检验的测试工作。

# 文档测试

doctest 非常有用,不但可以用来测试,还可以直接作为示例代码。通过某些文档生成工具,就可以自动把包含 doctest 的注释提取出来。用户看文档的时候,同时也看到了 doctest。

Python 内置的 “文档测试”(doctest)模块可以直接提取注释中的代码并执行测试.

class Dict(dict):
    """"
      这一段就是文档测试
       Simple dict but also support access as x.y style.
       >>> d1 = Dict()
       >>> d1['x'] = 100
       >>> d1.x
       100
       >>> d1.y = 200
       >>> d1['y']
       200
       >>> d2 = Dict(a=1, b=2, c='3')
       >>> d2.c
       '3'
       >>> d2['empty']
       Traceback (most recent call last):
           ...
       KeyError: 'empty'
       >>> d2.empty
       Traceback (most recent call last):
           ...
       AttributeError: 'Dict' object has no attribute 'empty'
       """
    def __init__(self, **kw):
        super(Dict, self).__init__(**kw)
    def __getattr__(self, key):
        try:
            return self[key]
        except KeyError:
            raise AttributeError(r"'Dict' object has no attribute '%s'" % key)
    def __setattr__(self, key, value):
        self[key] = value
if __name__ == '__main__':
    import doctest
    doctest.testmod()

将其中一个函数注释,运行让它报错

image.png

# IO 编程

程序和运行时的数据在内存中驻留

涉及到数据交换的地方,通常是磁盘、网络等,就需要 IO 接口

通常,程序完成 IO 操作会有 Input 和 Output 两个数据流

Stream(流)是一个很重要的概念,可以把流想象成一个水管,数据就是水管里的水,但是只能单向流动。

在 IO 编程中,就存在速度严重不匹配的问题。举个例子来说,比如要把 100M 的数据写入磁盘,CPU 输出 100M 的数据只需要 0.01 秒,可是磁盘要接收这 100M 数据可能需要 10 秒,怎么办呢?有两种办法:

image.png

# 同步 IO

第一种是 CPU 等着,也就是程序暂停执行后续代码,等 100M 的数据在 10 秒后写入磁盘,再接着往下执行,这种模式称为同步 IO;

# 异步 IO

另一种方法是 CPU 不等待,只是告诉磁盘,“您老慢慢写,不着急,我接着干别的事去了”,于是,后续代码可以立刻接着执行,这种模式称为异步 IO。

如果是服务员跑过来找到你,这是回调模式,如果服务员发短信通知你,你就得不停地检查手机,这是轮询模式。总之,异步 IO 的复杂度远远高于同步 IO。

# 文件读写

# 读文件 open()

传入文件名,标示符

参数:'rb' 二进制

encoding='gbk' 字符编码

f = open('/Users/michael/test.txt', 'r')

# read () 一次读取全部内容

f.read()
'Hello, world!'

# f.close()关闭文件

简化方法

# with open('filepath', 'r') as f: print(f.read())

Python 引入了 with 语句来自动帮我们调用 close() 方法,并且不必调用 f.close() 方法

with open('/path/to/file', 'r') as f:
    print(f.read())

如果文件很小, read() 一次性读取最方便;

如果不能确定文件大小,反复调用 read(size) 比较保险;

如果是配置文件,调用 readlines() 最方便

for line in f.readlines():
    print(line.strip()) # 把末尾的 '\n' 删掉

file 和缓存时 = 是 file-like Object 对象,不要求从特定类继承,只要写个 read() 方法就行

# f.write () 写文件

f = open('/Users/michael/test.txt', 'w')
f.write('Hello, world!')
f.close()

with open('/Users/michael/test.txt', 'w') as f:
    f.write('Hello, world!')

使用 with 语句操作文件 IO 是个好习惯

# StringIO 和 BytesIO

# StringIO

StringIO 顾名思义就是在内存中读写 str

from io import StringIO
 f = StringIO()
f.write('hello')

getvalue() 方法用于获得写入后的 str

from io import StringIO
>>> f = StringIO('Hello!\nHi!\nGoodbye!')
>>> while True:
...     s = f.readline()
...     if s == '':
...         break
...     print(s.strip())

# BytesIO

操作二进制数据,就需要使用 BytesIO

>>> from io import BytesIO
>>> f = BytesIO()
>>> f.write('中文'.encode('utf-8'))
6
>>> print(f.getvalue())
b'\xe4\xb8\xad\xe6\x96\x87'

# os 模块

# os.name 操作系统类型

# os.uname () 详细系统信息

# os.enciron 环境变量

要获取某个环境变量的值,可以调用 os.environ.get('key')

# 查看当前目录的绝对路径:
>>> os.path.abspath('.')
'/Users/michael'
# 在某个目录下创建一个新目录,首先把新目录的完整路径表示出来:
>>> os.path.join('/Users/michael', 'testdir')
'/Users/michael/testdir'
# 然后创建一个目录:
>>> os.mkdir('/Users/michael/testdir')
# 删掉一个目录:
>>> os.rmdir('/Users/michael/testdir')

通过 os.path.join() 函数,这样可以正确处理不同操作系统的路径分隔符

# os.path.join( ) 连接路径

# os.path.split() 拆分路径

# os.path.splitext() 文件扩展名

# 对文件重命名:
>>> os.rename('test.txt', 'test.py')
# 删掉文件:
>>> os.remove('test.py')

shutil 模块提供了 copyfile() 的函数,它们可以看做是 os 模块的补充

最后看看如何利用 Python 的特性来过滤文件。比如我们要列出当前目录下的所有目录,只需要一行代码:

>>> [x for x in os.listdir('.') if os.path.isdir(x)]
['.lein', '.local', '.m2', '.npm', '.ssh', '.Trash', '.vim', 'Applications', 'Desktop', ...]

要列出所有的 .py 文件,也只需一行代码:

>>> [x for x in os.listdir('.') if os.path.isfile(x) and os.path.splitext(x)[1]=='.py']
['apis.py', 'config.py', 'models.py', 'pymonitor.py', 'test_db.py', 'urls.py', 'wsgiapp.py']

# 序列化 pickle 模块

变量从内存中变成可存储或传输的过程称之为序列化,Python 中叫 pickling

变量内容从序列化的对象重新读到内存里称之为反序列化,即 unpickling

# pickle.dumps () 对象 -》字节 [序列化]

pickle.dumps() 方法把任意对象序列化成一个 bytes

pickle.dumps() 方法把任意对象序列化成一个 bytes , 并写入文件中

import pickle
d=dict(name='ruan',age=34,freand='woman')
# print(pickle.dumps(d))
f = open('timezone.txt', 'wb')
pickle.dump(d, f)
f.close()

# pickle.load () 字节 -》对象【反序列化】

import pickle
f=open(r'C:\Users\yangs\PycharmProjects\python_study\fun\timezone.txt','rb')
d=pickle.load(f)
f.close()
print(d)

image.png

image.png

# json 模块

json 模块的 dumps()loads() 函数是定义得非常好的接口的典范。

# json.dumps (python 对象) python 对象 -》json 对象

dumps() 方法返回一个 str ,内容就是标准的 JSON

# json.loads (json 对象) json 对象 -》python 对象

json.``dump (obj, fp, , skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan=True, cls=None, indent=None, separators=None, default=None, sort_keys=False, kw)*

# 类变为字典并序列化

json.dumps(s, default=lambda obj: obj.__dict__)

# 进程和线程

Python 的标准库提供了两个模块: _threadthreading_thread 是低级模块, threading 是高级模块

线程是最小的执行单元,而进程由至少一个线程组成

操作系统轮流让各个任务交替执行

真正的并行执行多任务只能在多核 CPU 上实现

对于操作系统来说,一个任务就是一个进程(Process),比如打开一个浏览器就是启动一个浏览器进程

Word,它可以同时进行打字、拼写检查、打印等事情。在一个进程内部,要同时干多件事,就需要同时运行多个 “子任务”,我们把进程内的这些 “子任务” 称为线程(Thread)

  • 多进程模式;
  • 多线程模式;
  • 多进程 + 多线程模式。

# 多进程

Unix/Linux 操作系统提供了一个 fork() 系统调用,普通的函数调用,调用一次,返回一次,但是 fork() 调用一次,返回两次,因为操作系统自动把当前进程(称为父进程)复制了一份(称为子进程),然后,分别在父进程和子进程内返回。

创建子进程

from multiprocessing import Process
import os
def run_pro(name):
    print('开始运行子进程%s,%s'%(name,os.getpid()))
if __name__ == '__main__':
    print('开始运行进程%s' % (os.getpid()))
    p=Process(target=run_pro,args=('test',))
    p.start()
    p.join()
    print('end')

image.png

# 启动大量子进程 pool

进程池

import time, threading
# 新线程执行的代码:
def loop():
    print('thread %s is running...' % threading.current_thread().name)
    n = 0
    while n < 5:
        n = n + 1
        print('thread %s >>> %s' % (threading.current_thread().name, n))
        time.sleep(1)
    print('thread %s ended.' % threading.current_thread().name)
print('thread %s is running...' % threading.current_thread().name)
t = threading.Thread(target=loop, name='LoopThread')
t.start()
t.join()
print('thread %s ended.' % threading.current_thread().name)

# 模块总结

# doctest 文档测试

# os.path 文件路径

# pickle 序列化

# json

使用 dict 和 set - 廖雪峰的官方网站 (liaoxuefeng.com)

用于学习记录,后期便于复习,参考链接

image.png

python file

image.png

image.png

调用脚本时会先载入 pyhton 解释器,然后运行脚本

rpm:软件管理包

操作符优先级:image.png

条件判断:

image.png

Python 为我们提供了非常完善的基础代码库,覆盖了网络、文件、GUI、数据库、文本等大量内容,被形象地称作 “内置电池(batteries included)”。用 Python 开发,许多功能不必从零编写,直接使用现成的即可。

语言定位:

Python 的定位是 “优雅”、“明确”、“简单”

Python 是解释型语言

image.png

# python

image.png

image.pngimage.png

image.png

# 数据类型

# int 整型

long int 长整型

int 整型

# float 浮点型

浮点数也就是小数,之所以称为浮点数,是因为按照科学记数法表示时

双精度浮点型 e

浮点型

# String 字符串

字符串是以单引号 ' 或双引号 " 括起来的任意文本

字符串是以 Unicode 编码

对于单个字符的编码,Python 提供了 ord() 函数获取字符的整数表示, chr() 函数把编码转换为对应的字符:

# Bool 布尔

True

Flase

# None 空值

None, None 不能理解为 0 ,因为 0 是有意义的,

Null 无意义

.

<iframe src="http://127.0.0.1:6806/widgets/brython-editor" data-src="http://127.0.0.1:6806/widgets/brython-editor" data-subtype="widget" border="0" frameborder="no" framespacing="0" allowfullscreen="true" style="width: 1341px; height: 276px;"></iframe>

# 变量

变量的概念基本上和初中代数的方程变量是一致的,

变量不仅可以是数字,还可以是任意数据类型。

变量名必须是大小写英文、数字和 _ 的组合,且不能用数字开头,字母或下划线开头

#变量类型
a=1
b='我是变量'
c=True
d=12.2e3
print(a,b,c,d)
print(type(a),type(b),type(c),type(d))

image.png

int a=1 静态语言 此时已经分配的 int 分区之后不能更改变量类型【不支持,Java】

a=3 动态语言,可以赋值成任意类型

# 动态定义

# 静态定义

a = 'ABC'
b = a
a = 'XYZ'
print(b)

执行 a = 'ABC' ,解释器创建了字符串 'ABC' 和变量 a ,并把 a 指向 'ABC'

py-var-code-1

执行 b = a ,解释器创建了变量 b ,并把 b 指向 a 指向的字符串 'ABC'

py-var-code-2

执行 a = 'XYZ' ,解释器创建了字符串 'XYZ',并把 a 的指向改为 'XYZ' ,但 b 并没有更改:

py-var-code-3

# 常量

所谓常量就是不能变的变量,通常用全部大写的变量名表示常量:

PI = 3.14159265359

# list 列表

list 是一种有序的集合,可以随时添加和删除其中的元素。

list=['a','b','c']
print(list)
print(len(list))

image.pngimage.png

# 切分

image.pngimage.png

# append()追加

str.append('a')

image.pngimage.png

# insert 插入指定位置

image.pngimage.png

# pop()删除末尾元素

要删除指定位置的元素,用 pop(i) 方法,其中 i 是索引位置

替换元素直接赋值即可

列表可以嵌套

s = ['python', 'java', ['asp', 'php'], 'scheme']

类型可以不同

L = ['Apple', 123, True]

# 切片

list [:-1] 不包含最后一个元素

list [:] 全部列表

list [::] 全部列表

image.pngimage.png

前 10 个数,每两个取一个

image.pngimage.png

# 列表生成

list(range(1,11))生成 10 个数 1-10

print([m+n for m in '123' for n in 'yza'])
k=[1,1,23,45,56,[1,12,3,467,[2,4,4,3,22]]]
print([x for x in k if x==1 ])

image.png

# tuple 元组

tuple 一旦初始化就不能修改

image.pngimage.png

但元组初始化后就不能进行更改了

b=(1)
#定义的不是 tuple,是 1 这个数!
# 这是因为括号 () 既可以表示 tuple,又可以表示数学公式中的小括号,
# 这就产生了歧义,因此,Python 规定,
# 这种情况下,按小括号进行计算,计算结果自然是 1
print(b)
print(type(b))
c=(1,)
print(c)
print(type(c))

image.png

image.png

# dict 字典

其他语言叫 map,使用键 - 值(key-value)存储,具有极快的查找速度。dict 的 key 必须是不可变对象。key 计算位置的算法称为哈希算法(Hash)。

# 定义

d={'name':'ruanyifen','age':60,'happy':'write'}
d['add']='Im add'
d['name']='fix'

image.png

# 取 value

# dict['key']

# 'key' in dict

# dict.get('key')

print(type(d))
print(d['name'])
print('name' in d)#方法一判断是否有这个主键在字典 d 中
print(d.get('name'))#方法二 取

image.png

# dict.pop ('key') 删除一个 key

image.png

# dict.keys 返回字典中所有 key 列表

# dict.update () 将 a 字典新 key,value 内容加入 b 字典中

dicta={"name":'ruan','age':20}
dictb={"name":'ruan2','age':40,'add':'w shi add'}
dictb.update(dicta)
print(dictb)

image.png

image.png

# 内建函数使用

type()

cmp()

len()

hash()

image.png

内建 cmp()函数比较两个 dict 时,先比较长度,后比值,输出 1 或 - 1

image.png

image.png

# dict 特点

dict 有以下几个特点:

  1. 查找和插入的速度极快,不会随着 key 的增加而变慢;
  2. 需要占用大量的内存,内存浪费多。

而 list 相反:

  1. 查找和插入的时间随着元素的增加而增加;
  2. 占用空间小,浪费内存很少。
    image.png
    image.png

# set 集合

也是一组 key 的集合,但不存储 value。由于 key 不能重复,所以,在 set 中,没有重复的 key。

重复元素在 set 中自动被过滤

# 定义

image.pngimage.png

image.pngimage.png

# set.add ('key') 添加元素

但重复元素不添加,自动去重

image.pngimage.png

# set.remove ('key') 删除元素

image.pngimage.png

set 可以看成数学意义上的无序和无重复元素的集合,

因此,两个 set 可以做数学意义上的交集、并集等操作:

# & 两个 set 交集

image.pngimage.pngimage.png

# | 两个 set 并集

image.pngimage.pngimage.png

image.png

# map () 的显示

打印 map 对象可以看到 map 对象返回的是一个地址,不是真实的数据

print(list(map对象))
print([it for it in map对象])

# 数据类型转换

# int()

# float()

# str()

# bool()

image.pngimage.png

# 条件判断

# if

if else

a=100
if a>=0:
    print(a)
else:
    print(-a)

image.png

if

if True:
    print('True')

image.png

if elif elif else

name='zhangsan'
if name=='zhangsan':
    print('我是',name)
elif name=='lisi':
    print('我是', name)
elif name=='wangwu':
    print('我是', name)
else:
    print('我谁的不是')

image.png

# input()输入输出

input () 返回的数据类型是 str

print()

# 循环 迭代

list,tuple,dict 都可循环

Python 的 for 循环本质上就是通过不断调用 next() 函数实现的,计算是惰性的

dict 循环按照 value 时:for value in dict.values

for value in d.values():
    print(value)

# for in

sum=0
for i in range(1,100):
    sum=sum+i
print(sum)

image.pngimage.pngimage.png

# while

sum2=0
k=0
while(k<100):
    sum2=sum2+k
    k=k+1
print(sum2)

image.png

# break

如果要提前结束循环,可以用 break 语句

# continue

通过 continue 语句,跳过当前的这次循环,直接开始下一次循环

# 生成器

在 Python 中,这种一边循环一边计算的机制,称为生成器:generator。

包括生成器和带 yield 的 generator function。

g = (x * x for x in range(10))

image.png

访问大文件

yield

# isinstance()迭代器

直接作用于 for 循环的对象统称为可迭代对象,都是迭代器 Iterable

listtupledictsetstr

listdictstr 虽然是 Iterable ,却不是 Iterator

listdictstrIterable 变成 Iterator 可以使用 **iter ()** 函数

以直接作用于 for 循环的数据类型有以下几种:

一类是集合数据类型,如 listtupledictsetstr 等;

一类是 generator ,包括生成器和带 yield 的 generator function。

可以使用 **isinstance ()** 判断一个对象是否是 Iterable 对象__iter__:

迭代对象

判断是不是可以迭代,用 Iterable

from collections import Iterable
isinstance({}, Iterable) --> True
isinstance((), Iterable) --> True
isinstance(100, Iterable) --> False

判断是不是迭代器,用 Iterator

from collections import Iterator
isinstance({}, Iterator)  --> False
isinstance((), Iterator) --> False
isinstance( (x for x in range(10)), Iterator)  --> True

Python 中 list,truple,str,dict 这些都可以被迭代,但他们并不是迭代器,为什么::因为和迭代器相比有一个很大的不同,list/truple/map/dict 这些数据的大小是确定的,也就是说有多少事可知的。但迭代器不是,迭代器不知道要执行多少次,所以可以理解为不知道有多少个元素,每调用一次 next (),就会往下走一步,是惰性的。

# 函数

抽象

将函数抽象成一个函数名称,不看内部结构直接调用方法

返回类型 函数名(输入参数):

函数体

# 调用函数

要调用一个函数,需要知道函数的名称和参数

绝对值 abs

image.pngimage.png

# 定义函数

def myabs(x):
    if x>0:
        return x
    if x<0:
        return -x
print(myabs(-100))

image.png

# 空函数

def nufun():
    pass

pass 可以用来作为占位符

# 函数 参数检查

def my_init_abs(x):
    if not isinstance(x,(int,float)):
        raise TypeError('no no no')
    else:
        if x>0:
            print(x)
        if x<0:
            print(-x)
my_init_abs(-90)

image.png

# 可返回多个值,函数

def return_much():
    a='返回'
    b='我也返回'
    c='我也要返回'
    return a,b,c
print(return_much())
print(type(return_much()))

image.png

# 函数参数

*args 是可变参数,args 接收的是一个 tuple;

**kw 是关键字参数,kw 接收的是一个 dict

power(x) 函数,参数 x 就是一个位置参数,可单个变量,list,set,tuple

power(*x) 函数,可传入单个变量,list,set,tuple,可以传入任意个参数或 0 个参数

power(**kw) 函数,字典 dict

可变参数允许你传入 0 个或任意个参数,这些可变参数在函数调用时自动组装为一个 tuple。

而关键字参数允许你传入 0 个或任意个含参数名的参数,这些关键字参数在函数内部自动组装为一个 dict.

power(x, n) ,用来计算 xn

power(x, n) 函数有两个参数: xn

默认参数,此时 age 和 city 为默认参数,可传值改变也可不变【不用传值】

power(L=None) 函数有 None 这个不变对象,可用 list

def enroll(name, gender, age=6, city='Beijing'):
    print('name:', name)
    print('gender:', gender)
    print('age:', age)
    print('city:', city)

image.png

# 必选参数

def a1(x):
    return x
print(a1(2))
print(a1(12.1))
print(a1('ruan'))
print(a1(True))
print(a1([1,2,3]))
print(a1({1,2,3}))
print(a1({"key":"vleaue",'name':'ruan','mun':23}))

image.png

# = 默认参数

def a2(x=9):
    return x
print(a2())
print(a2(2))
print(a2(12.1))
print(a2('ruan'))
print(a2(True))
print(a2([1,2,3]))
print(a2({1,2,3}))
print(a2({"key":"vleaue",'name':'ruan','mun':23}))

image.png

# * 可变参数

def a3(*x):
    return x
print(a3())
print(a3(2))
print(a3(12.1))
print(a3('ruan'))
print(a3(True))
print(a3([1,2,3]))
print(a3({1,2,3}))
print(a3({"key":"vleaue",'name':'ruan','mun':23}))

image.png

# ** 关键字参数

def a3(**kw):
    return kw
print(a3())
print(a3(kw=2))
print(a3(kw=12.1))
print(a3(kw='ruan'))
print(a3(kw=True))
print(a3(kw=[1,2,3]))
print(type(a3(kw=[1,2,3])))
print(a3(kw={1,2,3}))
print(type(a3(kw={1,2,3})))
print(a3(kw={"key":"vleaue",'name':'ruan','mun':23}))

image.png

# 递归函数

在函数内部,可以调用其他函数。

一个函数在内部调用自身本身,这个函数就是递归函数

使用递归函数需要注意防止栈溢出

#递归函数
def funmyself(x):
    if x>1:
        return x+funmyself(x-1)
    elif x==1:
        return 1
print(funmyself(3))

image.png

解决栈溢出方法:

尾递归优化,事实上尾递归和循环的效果是一样的

尾递归是指,在函数返回的时候,调用自身本身,并且,return 语句不能包含表达式

#尾递归
def funmyself2(n):
    return  funmyself2_it(n,1)
def  funmyself2_it(n,pro):
    if n==1:
        return pro
    else:
        return funmyself2(n-1)+n
print(funmyself2(100))

image.png

此时 funmyself2 是尾递归函数

# 转义字符 \

转义字符 \ 可以转义很多字符,比如

\n 表示换行,

\t 表示制表符,

字符 \ 本身也要转义,所以 \\ 表示的字符就是 \

Python 还允许用 r'' 表示 '' 内部的字符串默认不转义

# 运算符 and、or 和 not

运算优先级:not>or>and

# 除法 ///

print(10/3)
print(10//3)

# 除法一 / 浮点数

# 除法二 // 地板除 整数

/ 除法计算结果是浮点数,即使是两个整数恰好整除,结果也是浮点数:

// ,称为地板除,两个整数的除法仍然是整数:

# 取余 %

print(10%1)
print(10%3)
print(4%7)
print(2%20)
#如果 a% b a>b 则结果为 a

image.png

# 字符编码

ASCII 编码

8 个比特(bit)作为一个字节(byte)

一个字节能表示的最大的整数就是 255(二进制 11111111 = 十进制 255)

两个字节可以表示的最大整数是 65535 ,4 个字节可以表示的最大整数是 4294967295

大写字母 A 的编码是 65 ,二进制的 01000001 ,小写字母 z 的编码是 122

Unicode 把所有语言都统一到一套编码里,这样就不会再有乱码问题

ASCII 编码是 1 个字节,而 Unicode 编码通常是 2 个字节

ASCll 出现乱码问题引入 Unicode 编码存储空间多了一倍引入 UTF-8 编码

utf-8:将 Unicode 字符根据不同的数字大小编码成 1-6 个字节,常用的英文字母被编码成 1 个字节,汉字通常是 3 个字节,

字符ASCIIUnicodeUTF-8
A0100000100000000 0100000101000001
x01001110 0010110111100100 10111000 10101101

在计算机内存中,统一使用 Unicode 编码,当需要保存到硬盘或者需要传输的时候,就转换为 UTF-8 编码。

用记事本编辑的时候,从文件读取的 UTF-8 字符被转换为 Unicode 字符到内存里,编辑完成后,保存的时候再把 Unicode 转换为 UTF-8 保存到文件

image.png

浏览网页的时候,服务器会把动态生成的 Unicode 内容转换为 UTF-8 再传输到浏览器:

web-utf-8

# compile () 字符串编译为字节代码

# 编码转化

# ord ('A') 字母转字符

# chr (65) 字符转字母

a=ord('A')
b=chr(65)
print(a,b)

image.png

# b'str' 转为字节类型 bytes

bytes 类型的数据用带 b 前缀的单引号或双引号表示

x = b'ABC'

要注意区分 'ABC'b'ABC' ,前者是 str ,后者虽然内容显示得和前者一样,但 bytes 的每个字符都只占用一个字节。

image.pngimage.png

# str.encode('ascii') str 变为 bytes

ASCII

UTF-8

image.pngimage.png

# str.decode('utf-8') bytes 变为 str

print(type(b'abc'))
print(b'abc'.decode('utf-8'))
print(type(b'abc'.decode('utf-8')))

image.pngimage.png

len(str)计算字符数

函数计算的是 str 的字符数,如果换成 byteslen() 函数就计算字节数

print(len('abc'))
print(len(b'abc'))
print(len('中'))
print(len('中'.encode('utf-8')))

image.pngimage.png

# 特殊注释

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

第一行注释是为了告诉 Linux/OS X 系统,这是一个 Python 可执行程序,Windows 系统会忽略这个注释

第二行注释是为了告诉 Python 解释器,按照 UTF-8 编码读取源代码,否则,你在源代码中写的中文输出可能会有乱码。

# 占位符 格式化

# 占位符 % s % d % f

image.png

格式化方式和 C 语言是一致

% 运算符就是用来格式化字符串的。

在字符串内部,

%s 表示用字符串替换,

%d 表示用整数替换,

有几个 %? 占位符,后面就跟几个变量或者值,顺序要对应好。如果只有一个 %? ,括号可以省略

image.pngimage.png

占位符替换内容
%d整数
%f浮点数
%s字符串
%x十六进制整数

image.pngimage.png

转义: %% 来表示一个 %

# format()格式化字符串

image.png

image.png

# f-string 格式化字符串

{r} 被变量 r 的值替换, {s:.2f} 被变量 s 的值替换,并且 : 后面的 .2f 指定了格式化参数(即保留两位小数),因此, {s:.2f} 的替换结果是 19.62

image.pngimage.png

# 函数式编程

函数是 Python 内建支持的一种封装,通过层层函数进行调用

#面向过程的程序设计 #:把复杂任务分解成简单的任务,这种分解可以称之为面向过程的程序设计

函数式和函数的区别:

对比例子:计算和计算器的区别

编程语言,就是越低级的语言,越贴近计算机,抽象程度低,执行效率高,比如 C 语言;越高级的语言,越贴近计算,抽象程度高,执行效率低,比如 Lisp 语言

Python 不是纯函数式编程语言

函数式编程就是一种抽象程度很高的编程范式,

纯粹的函数式编程语言编写的函数没有变量,因此,任意一个函数,只要输入是确定的,输出就是确定的

# 函数式编程特点:

  1. 纯函数式编程语言函数没有变量,输入输出确定
  2. 允许本身作为参数传入另一个函数,允许返回一个函数

# 高阶函数

参数中有函数

高阶函数除了可以接受函数作为参数外,还可以把函数作为结果值返回

# 变量可以指向函数

a = 函数

求绝对值的函数 abs() 为例

print(abs(-10))
print(abs)

image.png

abs(-10)是函数调用,abs 是函数本身

k=abs(-20)
print(k)
#函数本身也可以赋值给变量
h=abs
print(h)
print(h(-100))

image.png

结论:函数本身也可以赋值给变量,即:# 变量可以指向函数。#

# 函数名也是变量

#函数名 #:其实就是指向函数的变量

a () 中 a 是指向函数 a()的变量

# 传入函数

既然变量可以指向函数,函数的参数能接收变量,那么一个函数就可以接收另一个函数作为参数,这种函数就称之为高阶函数。

#高阶函数 #:一个函数就可以接收另一个函数作为参数

b()

a(b)

x=a

def f(x):
    return abs(int(x))
def a(a,b,f):
    return f(a)+f(b)
if __name__ == '__main__':
    a1=input('a')
    a2=input('b')
    print(a(a1,a2,f))

image.png

此时函数 a 为高阶函数,需要调用 f 函数作为参数

# map/reduce 内建函数

内建了 map()reduce() 函数 高阶函数

# map()函数处理生成新 Iterator 迭代器

image.png 两个参数,函数名【函数本身】,需要处理的编程式 iterator

<br /> 创建一个迭代器,使用每个迭代器中的参数计算函数。当最短迭代用尽时停止。

map(func, *iterables) --> map object
def f(x):
    return x*x
r=map(f,[1,2,3,4,4,4,4,4,4,4,4])
print(r)
print(type(r))
print(list(r))
print(type(list(r)))

image.png

运算规则抽象

# reduce()函数作用在序列上

image.png

两个参数,函数名【函数本身】,需要处理的 #序列 #: sequence (序列) 是一组有顺序的元素的集合

序列基本样式 [下限:上限:步长]

reduce 把结果继续和序列的下一个元素做累积计算

reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)
from functools import reduce
>>> def fn(x, y):
...     return x * 10 + y
...
>>> reduce(fn, [1, 3, 5, 7, 9])
13579

image.png

# filter () 过滤序列

参数和 map()相似

filter() 也接收一个函数和一个序列

# sorted()排序

高阶函数image.png

参数:排序对象,key = 函数

sorted([36, 5, -12, 9, -21], key=abs)

排序的核心是比较两个元素的大小

print(sorted([1,2,353,6,3,234,43,435]))

image.png

key 指定绝对值大小排序

print(sorted([1,2,353,6,3,234,43,435,-242,-34,34,35],key=abs))

image.png

# 返回函数

# 函数作为返回值

高阶函数除了可以接受函数作为参数外,还可以把函数作为结果值返回

# 如果不需要立刻求和,而是在后面的代码中,
# 根据需要再计算怎么办?可以不返回求和的结果,而是返回求和的函数:
def zary_sum(a):
    def sum():
        sum1=0
        for i in a:
            sum1=sum1+i
        return sum1
    return sum
print(type(zary_sum([1,2,3,4])))
f=zary_sum([1,2,3,4])
print(f())

image.png

调用返回函数时,每次调用都会新生成一个函数

image.png

# 闭包

当一个函数的返回值是另外一个函数,

而返回的那个函数如果调用了其父函数内部的其它变量,如果 返回的这个函数在外部被执行,就产生了闭包

返回函数中,返回的函数调用父函数的内部变量

image.png

#返回函数
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(),f2(),f3())

image.png

返回一个函数时,牢记该函数并未执行,返回函数中不要引用任何可能会变化的变量。

# lambda()匿名函数

lambda 关键字 函数参数:函数表达式

传入函数时,有些时候,不需要显式地定义函数

Python 对匿名函数的支持有限,只有一些简单的情况下可以使用匿名函数。

lambda x:x*x
#等价于
def f(x):
   return x*x

关键字 lambda 表示匿名函数,冒号前面的 x 表示函数参数,只能一个表达式

不用写 return ,返回值就是该表达式的结果。

匿名函数也是一个函数对象

f=lamdba x:x*x

判断奇数函数

原函数:

def is_odd(n):
    return n % 2 == 1
L = list(filter(is_odd, range(1, 20)))

采用匿名函数修改

l=list(filter(lambda x:x%2==1,range(1,20)))
print(l)

image.png

# 装饰器 Decorator

# 本质上,装饰器就是一个返回函数的高阶函数

@log 等价于

now = log(now)

由于函数也是一个对象,而且函数对象可以被赋值给变量,

所以,通过变量也能调用该函数

image.pngimage.png

def log(func):
    def wrapper(*args,**kwargs):
        print('call %s'% func.__name__)
        return func(*args,**kwargs)
    return wrapper()
#func 为参数所以是高阶函数
#return 函数所以是返回函数,
#没有调用父函数中参数,所以不是闭包

image.pngimage.png

场景注意:

无 @装饰器时函数不调用,需要参数才调用

当 @时会直接调用装饰器定义函数然后执行函数,不用调用函数

三层时,传入参数

def log1(text):
    def decorator(func):
        def wapper(*args,**kw):
            print('%s %s'%(text,func.__name__))
            return func(*args,**kw)
        return wapper
    return decorator
@log1('ruan')
def now3():
    print("hhh")
now3()

相当于在返回高阶函数上还有一个函数,所以返回时应该还要调用一次

# @wraps 常用装饰器

当装饰器是个闭包时,装饰器调用变量会改变增加 @wraps 后装饰器内的变量不变

装饰器在装饰一个函数时,,原函数就成了一个新的函数,也

就是说其属性会发生变化,所以为了 不改变原函数的属性

我们会调用 functools 中的 wraps 装饰器来保证原函数的属性不变

# 不加 wraps 时

@wraps(func)
from functools import wraps
def wrap(func):
   
    def b():
        'b'
        print('decorator:',b.__name__)
        print('funname',func.__name__)
        func()
    return b
@wrap
def a():
    'a'
    print('name',a.__name__)
a()

image.png

加装饰器 wraps 时

from functools import wraps
def wrap(func):
    @wraps(func)
    def b():
        'b'
        print('decorator:',b.__name__)
        print('funname',func.__name__)
        func()
    return b
@wrap
def a():
    'a'
    print('name',a.__name__)
a()

image.png

闭包的概念:调用父函数中的变量的函数,为了保证数据安全。变量作用域只在函数内部,可在闭包中操作数据。

装饰器返回为什么是函数名(函数内存地址)而不直接执行函数?

当有参数传入时,可直接与调用的函数中的值传入参数执行。

()是运算符 f () 与 f.call () 等价:将 f 对象变成变成可调用的对象

# 偏函数(functools 模块)

属于 functools 模块

# 作用:

通过设定参数的默认值,降低函数调用的参数

int() 函数默认按十进制转换

print(int('100',base=8))

经常调用于是重写一个函数 int2

def int2(x, base=8):
    print(int(x, base))
    return int(x, base)
print(int2('2334'))

采用偏函数

import functools
int3=functools.partial(int,base=8)
print(int3('46'))
print(int())

image.png

functools.partial 的作用是将函数的特定参数固定住(设定为默认值)

image.png

创建偏函数的时候也可以接收,函数对象,*args,**kw

# 模块

python 包:作用区分相同名称的模块

模块相当于一个 py 文件

image.png

image.png

# 作用域

仅仅在模块内部使用。在 Python 中,是通过 _ 前缀来实现的。

# pubilc 公开

正常的函数和变量名是公开的(public)

# private 非公开_,__

_xxx 和__xxx 这样的函数或变量就是非公开的(private)

# 安装第三方模块 pip

pip install 模块名

# 模块搜索路径

import sys
print(sys.path)

两种方式:

  1. 添加搜索路径
    import sys
    sys.path.append('/Users/michael/my_py_scripts')
  2. 设置环境变量

第二种方法是设置环境变量 PYTHONPATH

# 面向对象编程

面向对象编程 ——Object Oriented Programming,简称 OOP,是一种程序设计思想

对象作为程序的基本单元,

一个对象包含了数据和操作数据的函数

数据封装、继承和多态是面向对象的三大特点

# 类和实例

面向对象最重要的概念就是类(Class)和实例(Instance)

类是抽象出来的模板

实例是根据类创建出的对象,每个对象可能有属性和方法

定义类是通过 class 关键字,类名通常是大写开头的单词

class Student(object):
    pass

image.png

!!!在类中定义函数有一点不同,定义佛如方法第一个参数永远是实例变量本身 self

仍然可以用默认参数、可变参数、关键字参数和命名关键字参数

# 数据封装

class Student(object):
    def __init__(self, name, score):
        self.name = name
        self.score = score
    def get_grade(self):
        if self.score >= 90:
            return 'A'
        elif self.score >= 60:
            return 'B'
        else:
            return 'C'

image.png

# 访问限制

# 作用:

确保了外部代码不能随意修改对象内部的状态

实例的变量名如果以 __ 开头,就变成了一个私有变量(private)

外部无法访问_name

class Student(object):
    def __init__(self,name,age):
        self._name=name
        self.age=age
    def print_name(self):
        print(self._name)
        return self.age,self._name
a=Student('ruan',23)
h=a.print_name()
print(h)

image.png

若是要获取,修改变量增加 get,set 方式即可

class Student(object):
    def __init__(self,name,age):
        self._name=name
        self.age=age 
    def get_name(self):
        return self.name
    def set_name(self,name):
        self._name=name

Python 本身没有任何机制阻止你干坏事,一切全靠自觉。

类外部无法访问

# 继承和多态

# 继承

# 多态

在继承关系中,如果一个实例的数据类型是某个子类,那它的数据类型也可以被看做是父类。

比如:动物是父类,狗和鱼是子类;鱼是鱼类,鱼是动物都成立。

判断一个变量是否是某个类型可以用 isinstance() 判断

# 鸭子类型

并不要求严格的继承体,一个对象只要 “看起来像鸭子,走起路来像鸭子”,那它就可以被看做是鸭子。

# 获取对象信息

# type()判断对象类型

image.png

# isinstance () 对于继承关系,判断 class 的类型

# dir()获取对象的所有属性和方法

# len()对象长度

# lower()返回小写的字符串

# getattr()获取属性 a

# setattr()设置属性 a

# hasattr(obj,'a')判断是否有属性 a

getattr(obj, 'z', 404) # 获取属性 'z',如果不存在,返回默认值 404
404

# 实例属性和类属性

image.png

class Student(object):
    name='ruan'
   
h=Student()
h.name='hhh'

类中的 name 是类属性,

创建 h 类对象即实例后赋值的是实例属性 name,但由于实例对象的优先级比类属性高,会屏蔽类中的 name 属性,即 h.name 的值为 hhh

# 总结:

  1. 实例属性属于各个实例所有,互不干扰;
  2. 类属性属于类所有,所有实例共享一个属性;
  3. 不要对实例属性和类属性使用相同的名字,否则将产生难以发现的错误

# 面向对象高级编程

数据封装、继承和多态只是面向对象程序设计中最基础的 3 个概念

多重继承、定制类、元类

# _slots_使用

可以给创建的实例绑定属性和方法

给一个实例绑定的方法对另外一个实例对象是不起作用的

class A:
    def run(self):
        print("i im ferther runing....")
sun1=A()
#给实例 sun1 设置 name 属性
sun1.name='i im name'
#创建实例对象 2
sun2=A()
#实例对象 sun1 的属性和 sun2 无关,即 sun2 没有 name 属性
#给实例 sun1 绑定方法,方法和属性同理
#定义方法
def setAll(self,num):
    print(num)
sun1.newfun=MethodType(setAll, sun1)
sun1.newfun(37)
#若所有实例都需要绑定方法则给类绑定方法
A.setAll=setAll
#给类绑定方法后,所有创建的实例的均可调用
def set_age(self, age): # 定义一个函数作为实例方法
...     self.age = age
...
>>> from types import MethodType
>>> s.set_age = MethodType(set_age, s) # 给实例绑定一个方法
>>> s.set_age(25) # 调用实例方法

# 限制实例属性,定义一个特殊的 __slots__ 变量

class Student(object):
    __slots__ = ('name', 'age') # 用 tuple 定义允许绑定的属性名称
s=Student()
s.name='ruan'
s.firstname='i im firstname'
#输出的时候 firstname 的属性会报错,
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Student' object has no attribute 'firstaname'

# 注意:

_slots_使用时要注意,定义的属性只在当前的类的实例中,对于继承的子类是不起作用的

class People(object):
    __slots__ = ('name','age')
    def run(self):
        print('i im run people......')
class Teacher(People):
    def say(self):
        print('i im teacher....')
t=Teacher()
t.tall='shouhua'
print(t.tall)
p=People()
p.tall('shouhuap')
print(p.tall)

image.png

只限制父类 People 的属性,而子类 Teacher 中不限制

# @property

在绑定属性时,如果我们直接把属性暴露出去,导致可以随意更改。通过 get,set 来获取更改属性值。

在 python 中直接调用装饰器将一个方法变成属性调用

class Student(object):
    @property
    #使用 get 方法是调用装饰器 @peoperty,
    # 同时自动创建了另一个装饰器 @属性.setter
    def score(self):
        return self.score
    @score.setter
    def score(self,value):
        self._score=value

# 总结:

- 权限限制只对类对象实际起作用,想要达到方法和属性强制访问权限,需要使用 @property 装饰器进行 get,set 方法

属性名与方法名一定要区分开,不然会进入死循环(self._age,def age ())
实例化的对象使用属性时,不是调用属性(meizi._age),而是用的方法名(meizi.age)
@property 其实就是实现了 getter 功能; @xxx.setter 实现的是 setter 功能;还有一个 @xxx.deleter 实现删除功能
定义方法的时候 @property 必须在 @xxx.setter 之前,且二者修饰的方法名相同(age ())
如果只实现了 @property(而没有实现 @xxx.setter),那么该属性为 只读属性

#请利用 @property 给一个 Screen 对象加上 width 和 height 属性,
# 以及一个只读属性 resolution:
class Screen(object):
    __slots__ = ('_width','_height','_resolution')
    @property
    def width(self):
        return self._width
    # 方法名称和实例变量均为 width:
    @width.setter
    def width(self,widthValue):
        self._width=widthValue
    @property
    def height(self):
        return self._height
    @width.setter
    def height(self, height):
        self._height = height
    @property
    def resolution(self):
        return self._width * self._height
s=Screen()
s.width=23
s.height=12
print(s.resolution)

image.png

s.score = 60 # OK,实际转化为 s.set_score (60)
s.score # OK,实际转化为 s.get_score ()

要特别注意:属性的方法名不要和实例变量重名。例如,以下的代码是错误的:

class Student(object):

    # 方法名称和实例变量均为birth:
    @property
    def birth(self):
        return self.birth

出现递归调用错误

image.png

之前的例子中 width 和_width 不同所以可以运行

# 多重继承

image.png

python 可以支持多继承,即一个子类可以继承多个父类;但 java 是单继承,只能有一个父类

Tercher(Name,study,teach)即 Teacher 可以继承多个父类

# MixIn

在设计类的继承关系时,通常,主线都是单一继承下来的,例如, Teacher 继承自 Name。但是,如果需要 “混入” 额外的功能,通过多重继承就可以实现,比如 Teacher 除了继承自 Name 外,再同时继承 Teach 。这种设计通常称之为 MixIn

Python 自带了 TCPServerUDPServer 这两类网络服务,而要同时服务多个用户就必须使用多进程或多线程模型,这两种模型由 ForkingMixInThreadingMixIn 提供。

# 多继承

多重继承这个名词一般用来形容继承链条可以很长,多个层次。

# 多重继承

多继承则指一个类可以有多个基类,相反则是单继承。任何面向对象编程语言都支持多重继承,但像 java 这种只能通过接口实现有限程度的多继承

问:多继承 如果多个类有共同得方法名 怎么区分是调得哪个类🤡

答:调用该方法的时候,会调用第一顺位继承父类的方法

# 总结:

  1. Python 允许使用多重继承,因此,MixIn 就是一种常见的设计
  2. 只允许单一继承的语言(如 Java)不能使用 MixIn 的设计

# 定制类

Python 的 class 中还有__xxx__有特殊用途的函数,可以帮助我们定制类

# str () 回用户看到的字符串

将对象 <__main__.Student object at 0x109afb190> 变成易读的数据

只在调用 print 时会调用__str__,交互界面时还是现实上方不易读的对象内容,此时用

# repr () 返回程序开发者看到的字符串

__str__() 返回用户看到的字符串,而 __repr__() 返回程序开发者看到的字符串,

也就是说, __repr__() 是为调试服务的

简写

def __str__(self):
        return 'xxx object (name=%s)' % self.name
__repr__ = __str__

# _iter () 返回一个迭代对象

需要用到 for in 迭代,需要转化为迭代对象

该方法返回一个迭代对象,然后,Python 的 for 循环就会不断调用该迭代对象的 __next__() 方法拿到循环的下一个值,直到遇到 StopIteration 错误时退出循环

例子:

class Fib(object):
    def __init__(self):
        self.a,self.b=0,1
    def __iter__(self):
        return self
    def __next__(self):
        self.a,self.b=self.b,self.a+self.b
        if self.a>1000:
            raise  StopIteration
        return self.a
a=Fib()
for i in a:
    print(i)

image.png

# getitem () 表现得像 list 那样按照下标取出元素

class Fib(object):
    def __init__(self):
        self.a,self.b=0,1
    def __iter__(self):
        return self
    def __next__(self):
        self.a,self.b=self.b,self.a+self.b
        if self.a>1000:
            raise  StopIteration
        return self.a
    def __getitem__(self, item):
        a,b=1,1
        for i in range(item):
            a,b=b,a+b
        return a
a=Fib()
print(a[3])

image.png

以上是传入 int,切片功能实现,isinstance 判断类型

class Fib(object):
    def __init__(self):
        self.a,self.b=0,1
    def __iter__(self):
        return self
    def __next__(self):
        self.a,self.b=self.b,self.a+self.b
        if self.a>1000:
            raise  StopIteration
        return self.a
    def __getitem__(self, item):
        if isinstance(item,int):
            a, b = 1, 1
            for i in range(item):
                a, b = b, a + b
            return a
        if isinstance(item,slice):
            start=item.start
            stop=item.stop
            if start is None:
                start=0
            a,b=1,1
            L=[]
            for x in range(stop):
                L.append(a)
                a,b=b,a+b
        return L
a=Fib()
print(a[3:12])

image.png

# getattr () 动态返回一个属性

调用类属性或方法时,先在__init__() 获取后,再从__getattr__() 获取,获取不到才报错

# call () 直接调用实例本身

与直接调用这个函数一样

class People(object):
    def __init__(self,name):
        self.name=name
    def __call__(self, *args, **kwargs):
        print('i im call %s'% self.name)
p=People('ruan')
p()

image.png

# 使用枚举类

枚举类:在某些情况下,一个类的 实例对象 的数量有限且固定 的,如季节类,它的实例对象只有春、夏、秋、冬。 在 Java 中像这种对象实例有限且固定的类被称为枚举类;这样的枚举类型定义一个 class 类型,然后,每个常量都是 class 的一个唯一实例。Python 提供了 Enum 类来实现这个功能。

from enum import Enum
M=Enum('a',('sun1','sun2','sun3','sun4'))
print(M.sun1)

image.png

自定义枚举类

from enum import Enum,unique
@unique
class Week(Enum):
    sun1=1
    sun2=2
    sun3=3
day2=Week.sun2
print(day2)

image.png

from enum import Enum,unique
@unique
class Gender(Enum):
    Male=0
    Female=1
class Student(object):
    def __init__(self,name,gender):
        self.name=name
        self.gender=gender
# 测试:
bart = Student('Bart', Gender.Male)
if bart.gender == Gender.Male:
    print('测试通过!')
else:
    print('测试失败!')

image.png

# 使用元类 [创建类]

实例对象是类创建

类是元类创建

创建类的方式

# 方式一:type()

type() 函数既可以返回一个对象的类型,又可以创建出新的类型,比如,我们可以通过 type() 函数创建出 Hello

from class1104 import *
h=Hello()
print(type(h))
print(type(Hello))

image.png

Hello = type('Hello', (object,), dict(hello=fn))

要创建一个 class 对象, type() 函数依次传入 3 个参数:

  1. class 的名称;
  2. 继承的父类集合,注意 Python 支持多重继承,如果只有一个父类,别忘了 tuple 的单元素写法;
  3. class 的方法名称与函数绑定,这里我们把函数 fn 绑定到方法名 hello

# 方式二:元类 metaclass

先定义 metaclass,然后创建类。

先定义类,然后创建实例。

metaclass 是 Python 面向对象里最难理解,也是最难使用的魔术代码。

按照默认习惯,metaclass 的类名总是以 Metaclass 结尾,以便清楚地表示这是一个 metaclass

# metaclass 采用 type 创建类 ,metaclass 是类的模板,所以必须从 `type` 类型派生
class ListMetaclass(type):
    def __new__(cls, name,bases,attrs):
        attrs['add']=lambda self, value:self.append(value)
        return type.__new__(cls,name,bases,attrs)
class MyList(list,metaclass=ListMetaclass):
    pass
mylist=MyList()
mylist.add(1)
print(mylist)

image.png

__new__() 方法接收到的参数依次是:

  1. 当前准备创建的类的对象;
  2. 类的名字;
  3. 类继承的父类集合;
  4. 类的方法集合

# 应用场景

ORM 全称 “Object Relational Mapping”,即对象 - 关系映射,就是把关系数据库的一行映射为一个对象,也就是一个类对应一个表,这样,写代码更简单,不用直接操作 SQL 语句。

要编写一个 ORM 框架,所有的类都只能动态定义,因为只有使用者才能根据表的结构定义出对应的类来。

# 错误处理 try

try:
    print('try...')
    r = 10 / 0
    print('result:', r)
except ValueError as e:
    print('ValueError:', e)
except ZeroDivisionError as e:
    print('ZeroDivisionError:', e)e)
finally:
    print('finally...')
print('END')

Python 的错误其实也是 class,所有的错误类型都继承自 BaseException

UnicodeErrorValueError 的子类🤡

Built-in Exceptions — Python 3.10.0 documentation

# 调用栈

让 Python 解释器来打印出错误堆栈

image.png

# 记录错误 logging

可将 logging 生成一个 txt 方便查看

try:
        xxx
    except Exception as e:
        logging.exception(e)

# 抛出错误 raise

except ValueError as e:
        print('ValueError!')
        raise

bar() 函数中,我们明明已经捕获了错误,但是,打印一个 ValueError! 后,又把错误通过 raise 语句抛出去了,这不有病么?

其实这种错误处理方式不但没病,而且相当常见。捕获错误目的只是记录一下,便于后续追踪。但是,由于当前函数不知道应该怎么处理该错误,所以,最恰当的方式是继续往上抛,让顶层调用者去处理。

# 调试方法

# 1. print()

# 2. 断言 assert

assert n != 0, 'n is zero!'

assert 的意思是,表达式 n != 0 应该是 True ,否则,根据程序运行的逻辑,后面的代码肯定会出错。

采用断言的好处:

启动 Python 解释器时可以用 -O 参数来关闭 assert

$ python -O err.py

image.png

关闭后,你可以把所有的 assert 语句当成 pass 来看

# 3. logging

import logging
s = '0'
n = int(s)
logging.info('n = %d' % n)
print(10 / n)

# 4.pbd 单步执行

启动 Python 的调试器 pdb,让程序以单步方式运行,可以随时查看运行状态。

python -m pdb xxx.py
(Pbd) 1#查看第一行代码,单步执行第一行代码

# 5. pdb.set_trace()

这个方法也是用 pdb,但是不需要单步执行,我们只需要 import pdb ,然后,在可能出错的地方放一个 pdb.set_trace() ,就可以设置一个断点:

import pdb
s = '0'
n = int(s)
pdb.set_trace() # 运行到这里会自动暂停
print(10 / n)

可以用命令 p 查看变量,或者用命令 c 继续运行:

image.png

# 6.IDE 工具

vscode,pycharm....

# 单元测试

单元测试是用来对一个模块、一个函数或者一个类来进行正确性检验的测试工作。

# 文档测试

doctest 非常有用,不但可以用来测试,还可以直接作为示例代码。通过某些文档生成工具,就可以自动把包含 doctest 的注释提取出来。用户看文档的时候,同时也看到了 doctest。

Python 内置的 “文档测试”(doctest)模块可以直接提取注释中的代码并执行测试.

class Dict(dict):
    """"
      这一段就是文档测试
       Simple dict but also support access as x.y style.
       >>> d1 = Dict()
       >>> d1['x'] = 100
       >>> d1.x
       100
       >>> d1.y = 200
       >>> d1['y']
       200
       >>> d2 = Dict(a=1, b=2, c='3')
       >>> d2.c
       '3'
       >>> d2['empty']
       Traceback (most recent call last):
           ...
       KeyError: 'empty'
       >>> d2.empty
       Traceback (most recent call last):
           ...
       AttributeError: 'Dict' object has no attribute 'empty'
       """
    def __init__(self, **kw):
        super(Dict, self).__init__(**kw)
    def __getattr__(self, key):
        try:
            return self[key]
        except KeyError:
            raise AttributeError(r"'Dict' object has no attribute '%s'" % key)
    def __setattr__(self, key, value):
        self[key] = value
if __name__ == '__main__':
    import doctest
    doctest.testmod()

将其中一个函数注释,运行让它报错

image.png

# IO 编程

程序和运行时的数据在内存中驻留

涉及到数据交换的地方,通常是磁盘、网络等,就需要 IO 接口

通常,程序完成 IO 操作会有 Input 和 Output 两个数据流

Stream(流)是一个很重要的概念,可以把流想象成一个水管,数据就是水管里的水,但是只能单向流动。

在 IO 编程中,就存在速度严重不匹配的问题。举个例子来说,比如要把 100M 的数据写入磁盘,CPU 输出 100M 的数据只需要 0.01 秒,可是磁盘要接收这 100M 数据可能需要 10 秒,怎么办呢?有两种办法:

image.png

# 同步 IO

第一种是 CPU 等着,也就是程序暂停执行后续代码,等 100M 的数据在 10 秒后写入磁盘,再接着往下执行,这种模式称为同步 IO;

# 异步 IO

另一种方法是 CPU 不等待,只是告诉磁盘,“您老慢慢写,不着急,我接着干别的事去了”,于是,后续代码可以立刻接着执行,这种模式称为异步 IO。

如果是服务员跑过来找到你,这是回调模式,如果服务员发短信通知你,你就得不停地检查手机,这是轮询模式。总之,异步 IO 的复杂度远远高于同步 IO。

# 文件读写

# 读文件 open()

传入文件名,标示符

参数:'rb' 二进制

encoding='gbk' 字符编码

f = open('/Users/michael/test.txt', 'r')

# read () 一次读取全部内容

f.read()
'Hello, world!'

# f.close()关闭文件

简化方法

# with open('filepath', 'r') as f: print(f.read())

Python 引入了 with 语句来自动帮我们调用 close() 方法,并且不必调用 f.close() 方法

with open('/path/to/file', 'r') as f:
    print(f.read())

如果文件很小, read() 一次性读取最方便;

如果不能确定文件大小,反复调用 read(size) 比较保险;

如果是配置文件,调用 readlines() 最方便

for line in f.readlines():
    print(line.strip()) # 把末尾的 '\n' 删掉

file 和缓存时 = 是 file-like Object 对象,不要求从特定类继承,只要写个 read() 方法就行

# f.write () 写文件

f = open('/Users/michael/test.txt', 'w')
f.write('Hello, world!')
f.close()

with open('/Users/michael/test.txt', 'w') as f:
    f.write('Hello, world!')

使用 with 语句操作文件 IO 是个好习惯

# StringIO 和 BytesIO

# StringIO

StringIO 顾名思义就是在内存中读写 str

from io import StringIO
 f = StringIO()
f.write('hello')

getvalue() 方法用于获得写入后的 str

from io import StringIO
>>> f = StringIO('Hello!\nHi!\nGoodbye!')
>>> while True:
...     s = f.readline()
...     if s == '':
...         break
...     print(s.strip())

# BytesIO

操作二进制数据,就需要使用 BytesIO

>>> from io import BytesIO
>>> f = BytesIO()
>>> f.write('中文'.encode('utf-8'))
6
>>> print(f.getvalue())
b'\xe4\xb8\xad\xe6\x96\x87'

# os 模块

# os.name 操作系统类型

# os.uname () 详细系统信息

# os.enciron 环境变量

要获取某个环境变量的值,可以调用 os.environ.get('key')

# 查看当前目录的绝对路径:
>>> os.path.abspath('.')
'/Users/michael'
# 在某个目录下创建一个新目录,首先把新目录的完整路径表示出来:
>>> os.path.join('/Users/michael', 'testdir')
'/Users/michael/testdir'
# 然后创建一个目录:
>>> os.mkdir('/Users/michael/testdir')
# 删掉一个目录:
>>> os.rmdir('/Users/michael/testdir')

通过 os.path.join() 函数,这样可以正确处理不同操作系统的路径分隔符

# os.path.join( ) 连接路径

# os.path.split() 拆分路径

# os.path.splitext() 文件扩展名

# 对文件重命名:
>>> os.rename('test.txt', 'test.py')
# 删掉文件:
>>> os.remove('test.py')

shutil 模块提供了 copyfile() 的函数,它们可以看做是 os 模块的补充

最后看看如何利用 Python 的特性来过滤文件。比如我们要列出当前目录下的所有目录,只需要一行代码:

>>> [x for x in os.listdir('.') if os.path.isdir(x)]
['.lein', '.local', '.m2', '.npm', '.ssh', '.Trash', '.vim', 'Applications', 'Desktop', ...]

要列出所有的 .py 文件,也只需一行代码:

>>> [x for x in os.listdir('.') if os.path.isfile(x) and os.path.splitext(x)[1]=='.py']
['apis.py', 'config.py', 'models.py', 'pymonitor.py', 'test_db.py', 'urls.py', 'wsgiapp.py']

# 序列化 pickle 模块

变量从内存中变成可存储或传输的过程称之为序列化,Python 中叫 pickling

变量内容从序列化的对象重新读到内存里称之为反序列化,即 unpickling

# pickle.dumps () 对象 -》字节 [序列化]

pickle.dumps() 方法把任意对象序列化成一个 bytes

pickle.dumps() 方法把任意对象序列化成一个 bytes , 并写入文件中

import pickle
d=dict(name='ruan',age=34,freand='woman')
# print(pickle.dumps(d))
f = open('timezone.txt', 'wb')
pickle.dump(d, f)
f.close()

# pickle.load () 字节 -》对象【反序列化】

import pickle
f=open(r'C:\Users\yangs\PycharmProjects\python_study\fun\timezone.txt','rb')
d=pickle.load(f)
f.close()
print(d)

image.png

image.png

# json 模块

json 模块的 dumps()loads() 函数是定义得非常好的接口的典范。

# json.dumps (python 对象) python 对象 -》json 对象

dumps() 方法返回一个 str ,内容就是标准的 JSON

# json.loads (json 对象) json 对象 -》python 对象

json.``dump (obj, fp, , skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan=True, cls=None, indent=None, separators=None, default=None, sort_keys=False, kw)*

# 类变为字典并序列化

json.dumps(s, default=lambda obj: obj.__dict__)

# 进程和线程

Python 的标准库提供了两个模块: _threadthreading_thread 是低级模块, threading 是高级模块

线程是最小的执行单元,而进程由至少一个线程组成

操作系统轮流让各个任务交替执行

真正的并行执行多任务只能在多核 CPU 上实现

对于操作系统来说,一个任务就是一个进程(Process),比如打开一个浏览器就是启动一个浏览器进程

Word,它可以同时进行打字、拼写检查、打印等事情。在一个进程内部,要同时干多件事,就需要同时运行多个 “子任务”,我们把进程内的这些 “子任务” 称为线程(Thread)

  • 多进程模式;
  • 多线程模式;
  • 多进程 + 多线程模式。

# 多进程

Unix/Linux 操作系统提供了一个 fork() 系统调用,普通的函数调用,调用一次,返回一次,但是 fork() 调用一次,返回两次,因为操作系统自动把当前进程(称为父进程)复制了一份(称为子进程),然后,分别在父进程和子进程内返回。

创建子进程

from multiprocessing import Process
import os
def run_pro(name):
    print('开始运行子进程%s,%s'%(name,os.getpid()))
if __name__ == '__main__':
    print('开始运行进程%s' % (os.getpid()))
    p=Process(target=run_pro,args=('test',))
    p.start()
    p.join()
    print('end')

image.png

# 启动大量子进程 pool

进程池

import time, threading
# 新线程执行的代码:
def loop():
    print('thread %s is running...' % threading.current_thread().name)
    n = 0
    while n < 5:
        n = n + 1
        print('thread %s >>> %s' % (threading.current_thread().name, n))
        time.sleep(1)
    print('thread %s ended.' % threading.current_thread().name)
print('thread %s is running...' % threading.current_thread().name)
t = threading.Thread(target=loop, name='LoopThread')
t.start()
t.join()
print('thread %s ended.' % threading.current_thread().name)

# 模块总结

# doctest 文档测试

# os.path 文件路径

# pickle 序列化

# json

更新于 阅读次数