栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Python

python随笔 (langya)

Python 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

python随笔 (langya)

python笔记

本篇系准备考核,时间仓促,格式上还未整理,请谅解。

仅根据本人目前所欠缺整理,部分内容没有设计。

命令行模式下python hello.py 运行python程序

.py文件只能在命令行模式下执行

能否像.exe文件那样直接运行.py文件?

Windows上不可以,Mac和Linux上可以,方法是在.py文件的第一行加上一个特殊的注释:

#!/usr/bin/env python3

然后通过命令给 hello.py 以执行权限

$ chmod a+x hello.py

然后就可以直接运行hello.py了

print函数可以接受多个字符串,用逗号隔开,就可以连成一串输出:

print("liu","jing","yu")

输出:liu jing yu

对于很大的数,例如:10000000000,很难表示清楚0的个数

python中允许在数字之间以_分隔

所以写成10_000_000_000和10000000000是一样的。

很多字符都需要转义,就需要加很多,为了简化,python还允许r’‘表示’'内部的字符串默认不转义

print(r'\\t\')
#输出	\\t\

如果字符串内部有很多换行,用n写在一行里不好阅读,python允许用’’’…’’'的格式表示多行内容

print('''line1
line2
line3''')
#输出:line1
#     line2
#			line3

两个方法,ord()函数获取字符的整数表示,chr()函数把编码转换为对应的字符

python的字符串类型是str,在内存中以Unicode表示,一个字符对应若干个字节。如果要在网络上传输,或者保存到磁盘上,就需要把str变为以字节为单位的bytes

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

x=b'ABC' #bytes类型,每个字符只占用一个字节
x='ABC' #str类型,一个字符对应若干个字节

以Unicode表示的str通过encode()方法可以编码为指定的bytes

'ABC'.encode('ascii')
# b'ABC'
'中文'.encode('utf-8')
# b'xe4xb8xadxe6x96x87'

纯英文的str可以用ASCII编码为bytes,含有中文的str可以用UTF-8编码为bytes,但是无法用ASCII编码。

在bytes中,无法显示为ASCII字符的字节,用x##显示

如果从网络或磁盘上读取了字节流,那么读到的数据就是bytes,要把bytes变为str,就需要使用decode()方法:

b'ABC'.decode('ascii')
# 'ABC'
b'xe4xb8xadxe6x96x87'.decode('utf-8')
# '中文'

如果bytes中只有一小部分无效的字节,可以传入errors='ignore’忽略错误的字节

b'xe4xb8xadxff'.decode('utf-8', errors='ignore')
# '中'

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

len('ABC')
# 3
len(b'中文')
# 6

一个中文经过UTF-8编码后通常会占用3个字节

操作字符串时,为了避免乱码问题,应始终坚持使用UTF-8编码对str和bytes进行转换。

当源代码中包含有中文的时候,在保存源代码时,就需要务必指定保存为UTF-8编码,当Python解释器读取源代码时,为了让它按UTF-8编码读取,加上以下两行

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

第一行是为了告诉Linux/OS X系统,这是一个python的可执行程序,windows系统会忽略

第二行注释是为了告诉python解释器,按照UTF-8编码读取源代码,否则,可能会出现乱码

同时要保证使用的文本编辑器正在使用UTF-8 without BOM编码

格式化

第一种方式和C语言最接近

格式化整数个浮点数还可以指定是否补0和整数与小数的位数

print('%2d-%02d' % (3, 1))
print('%.2f' % 3.1415926)
# 3-01
#3.14

如果不确定应该用什么,%s永远起作用,它会把任何数组转换为字符串

%%来表示一个%

另一种格式化的方法

使用字符串的format()方法,他会用传入的参数一次替换字符串内的占位符{0}.{1}…

'hello,{0},成绩提升了{1:.1f}%'.format('小明',17,125)

最后一种格式化方法 f-string

如果包含{xxx},就会以相应的变量替换

r=2.5
s=2.5*r**2
print(f'The area of a circle with radius {r} is {s:.2f}')
#格式化输出

list是一个可变的有序表

classmates = ['Michael', 'Bob', 'Tracy']
#往list种追加元素到末尾
classmates.append('Adam')
classmates.insert(1,'Jack')
#删除元素
classmates.pop()
classmates.pop(i)
#更改元素
classmates[1]='Sarah'

list中的元素的数据类型可以不同,元素也可以是另一个list

p = ['asp', 'php']
s = ['python', 'java', p, 'scheme']
#s[2][1]就表示'php'

元组 tuple(和list区别:一旦初始化就不能再修改)

元组用( )表示

t=(1)

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

只有一个元素的tuple定义时,必须加一个逗号,来消除歧义

t=(1,)

可变的tuple

t=('a','b',['A','B'])
t[2][0]='X'
t[2][1]='Y'

tuple指向的list并没有改变成其他的list,所以tuple所谓的不变是说,tuple的每个元素,指向永远不变,而list本身是可变的。

elif就是else if的缩写

注意:不要少写来冒号:

age=3
if age>=18:
  print('adult')
elif age>=6:
  print('teenager')
else:
  print('kid')

if x:

只要x是非零数值,非空字符串,非空list等,就判断为True,否则判断为False

input()返回的数据类型是str,str不能直接和整数比较,必须先把str转换成整数。Python提供来int()函数来完成这件事情

s=input('birth:')
birth=int(s)
if birth<2000:
  print('00前')
else:
  print('00后')

int()发现一个字符串不是合法的数字时就会报错,程序就退出了

循环

names = ['Michael', 'Bob', 'Tracy']
for name in names:
  print(name)

range()函数可以生成一个整数序列,再通过list()函数可以转换成list

list(range(5))
#[0,1,2,3,4]

三种方式再次总结一下

L = ['Bart', 'Lisa', 'Adam']
for name in L:
    print('Hello,%s'%name)
for name in L:
    print('Hello,{0}'.format(name))
for name in L:
    print(f'Hello,{name}')

python内置了字典:dict在其他语言中也称为map,使用键值(key-value)存储,具有极快的查找速度

d={'Michael':95,'Bob':75,'Tracy':85}
d['Michael']
#95

避免key不存在的错误,有两种方法,一种是通过in判断key是否存在

'Thomas' in d
#False

另一种是通过dict提供的方法get(),如果key不存在返回None,或者是自己指定的value

d.get('Thomas')
 # None,返回None是时,python的交互环境不显示结果
d.get('Thamas',-1)
#-1

删除一个key用pop(key)的方法,对应value也会被删除

dict内部存放的顺序和key放入的顺序没有关系

dict特点:查找和插入的速度极快,不会随着key的增加而变慢,需要占用大量的内存,内存浪费多

list相反:查找和插入的时间随着元素的增加而增加:占用空间小,浪费内存很少

dict是一种用空间换时间的方法

dict的key必须是不可变对象

通过key计算位置的算法称为哈希算法

python中,字符串和整数等都是不可变的,因此可以放心地作为key,而list是可变的,就不能作为key

set也是一组key的集合,但是不存储value,在set中没有重复的key,并且没有顺序

s=set([1,2,3])
# {1,2,3}

传入的参数是一个list

通过add(key)方法可以添加元素

通过remove(key)方法可以删除元素

两个set可以做数学意义上的交集并集操作

s1=set([1,2,3])
s2=set([2,3,4])
s1&s2
# {2,3}
s1|s2
# {1,2,3,4}

set和dict的唯一区别在于没有存储对应的value

list不能放入set

对于不可变对象来说,调用自身的任意方法,也不会改变对对象自身的内容。相反,这些方法会创建新的对象并返回,这样,就保证了不可变对象本身永远是不可变的

tuple虽然是不可变对象,(1,2,3)可以放入set中,但是(1,2,[3,4])不能放入set中

tuple中有list不可以作为key,没有list时,也可以作为key

数据类型转换-很灵活

int('123')
#123
float('12.34')
#12.34
str(1.23)
#'1.23'

函数名就是指向一个函数对象的引用,完全可以把函数名赋给另一个变量,相当于给函数起了一个“别名”

a=abs
a(-1)
#1

python中,定义一个函数用def,依次写出函数名、括号、括号中的参数和冒号:

函数执行完毕后也会返回结果,只是结果为None,return None也可以简写为return

空函数

pass语句什么也不做,可以用来作为占位符,比如现在还没想好怎么写函数的代码,就可以先放一个pass

if age>=18:
  pass

如果没有pass,就会有语法错误

参数检查

内置函数isinstance(),用来判断一个对象是否是已知的类型

def my_abs(x):
  if not isinstance(x,(int,float)):#参数检查
    raise TypeError('bad operand type')
  if x>=0:
    return x
  else:
    return -x

返回多个值

import math
def move(x,y,step,angle=0):
  nx=x+step*math.cos(angle)
  ny=y+step*math.sin(angle)
  return nx,ny

x,y=move(100,100,60,math.pi/6)

本质:

返回值仍然是单一值,返回值是一个tuple,但是在语法上,返回一个tuple可以省略括号,而且多个变量可以同时接收一个tuple,按位置赋给对应的值,所以python的函数返回值其实就是一个tuple,写起来更加方便

当有多个参数时,把变化大的参数放在前面,变化小的参数放在后面。变化小的参数就可以作为默认参数。

也可以不按顺序提供默认参数。当不按顺序提供部分默认参数时,就需要把参数名字写上。

默认参数的坑

def add_end(L=[]):
  L.append('END')
  return L

add_end()# END
add_end()# END END
add_end()# END END END

但是再次调用add_end()时,结果就不对了

python函数在定义的时候,默认参数L的值就被计算出来了,即[],因为默认参数L也是一个变量,它指向对象[],每次调用该函数,如果改变了L的内容,则下次再调用时,默认参数的内容就变了,不再是函数定义时的[ ]了

定义默认参数时,牢记:默认参数必须指向不变对象!(尽量不要用列表)

修改

def add_end(L=None):
  if L is None:
    L=[]
  L.append('END')
  return L

可变参数

由于参数的个数不确定,首先想到可以把a,b,c等作为一个list或者tuple传进来

def cal(numbers):
  sum=0
  for n in numbers:
    sum+=n*n
  return n

cal([1,2,3])
cal((1,2,3,4))

可以把函数的参数改为可变参数

def cal(*numbers):
  sum=0
  for n in numbers:
    sum+=n*n
  return sum

定义可变参数以及定义一个list或tuple相比,仅仅在参数前面加了一个*号,在函数内部,参数numbers接收到的是一个tuple,因此,函数代码完全不变,嗲用函数时,可以传入任意个参数。但是,调用该函数时,可以传入任意个参数,包括0个参数

如果已经有一个list或者tuple,调用一个可变参数怎么办?

nums=[1,2,3]
calc(num[0],num[1],num[2])
#上面这种方式太繁琐
calc(*nums)

*nums表示把nums这个list所有的元素作为可变参数传进去,这种写法相当有用,而且很常见。

可变参数在函数调用时自动组装为一个tuple

关键字参数

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

def person(name,age,**kw):
  print('name:',name,'age',age,'other',kw)
  
person('Bob',35,city='Beijing')
person('Adam',45,gender='M',job='engineer')
extra={'city':'Beijing','jib':'Enginner'}
person('Jack',24,city=extra['city'],job=extra['job'])
#这种方式太复杂了
person('Jack',24,**extra)

* 传入list、tuple **传入dict

命名关键字参数

def person(name,age,**kw):
  if 'city' in kw:
    pass
  if 'job' in kw:
    pass
  print('name',name,'age',age,'other',kw)

如果要限制关键字参数的名字,就可以用命名关键字参数,例如,只接收city和job作为关键字参数。这种方式定义的函数如下

def person(name,age,*,city,job):
  print('name',name,'age',age,'city',city,'job',job)

和关键字参数**不同,明明关键字参数需要一个特殊分隔符*,*后面的参数被视为命名关键字参数

person('jack',25,city='Beijing',job='Engineer')

如果函数定义中已经有了一个可变参数,后面跟着的命名关键字参数就不再需要一个特殊分隔符*了

def person(name,age,*args,city,job):
  print(name,age,args,city,job)

命名关键字参数必须传入参数名,这和位置参数不同,如果没有传入参数名,调用会报错

person('jack',24,'Beijing','Engineer')
#缺少参数名city和job,python将后两个参数传给*args,但缺少命名关键字参数报错

命名关键字参数可以有缺省值,从而简化

def person(name,age,*,city='Beijing',job):
  print(name,age,city,job)
#city有默认值,可以不传入city参数

如果没有可变参数,就必须添加一个*作为特殊分隔符,如果缺少*,python解释器无法识别位置参数和命名关键字参数,缺少时,认为是位置参数

参数组合

在python中定义函数,按照必选参数,默认参数,可变参数,和命名关键字参数,关键字参数

def f1(a,b,c=0,*args,**kw):
  pass
def f2(a,b,c=0,*,d,**kw):
  pass
def fact(n):
  return fact_iter(n,1)
def fact_iter(num,product):
  if num==1:
    return product
  return fact_iter(num-1,num*product)

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

n*fact(n-1)引入了乘法表达式,所以就不是尾递归了。

切片(Slice)

L = ['Michael', 'Sarah', 'Tracy', 'Bob', 'Jack']
L[0:3]
#['Michael', 'Sarah', 'Tracy']
#从0开始,到3为止,不包括3,如果第一个索引是0,还可以省略
#和  L[:3]  效果一样
L[-2:]
#['Bob', 'Jack'] 从倒数第二个开始
L=list(range(100))
#[0,1,2,3,...,99]
L[:10:2] #0-9每两个取一个
#0,2,4,6,8
L[::5] #0-99每5个取一个
#0,5,10,15,20,25,30.....,85,90
L[:] #原封不动,复制一个list

tuple也是一种list,唯一的区别是tuple不可变,tuple的切片操作的结果仍是tuple

字符串’xxx’也是一种list,每个元素就是一个字符,因此字符串也可以用切片操作,只是结果仍是字符串

python没有针对字符串的截取函数,只需要一个切片操作就可以完成,非常简单。

迭代:

如果给定一个list或者tuple,我们可以通过for循环来遍历整个list或者tuple,这种遍历我们称为迭代

在python中,迭代是通过for…in来完成的,在C语言中迭代是通过下标完成的。

python的for循环不仅可以用在list或tuple上,还可以作用在其他可迭代对象上。

list这种数据类型虽然有下标,但很多其他数据类型是没有下标的,但是,只要是可以迭代对象,无论有没有下标,都可以迭代,比如,dict就可以迭代:

d={'a':1,'b':2,'c':3}
for key in d:
  print(key)
# a c b

dict的存储不是按照list的方式顺序存储,所以,迭代出的结果顺序很可能不一样。

默认情况下,dict迭代的是key,如果要迭代value,可以用for value in d.values(),吐过要同时迭代key和value,可以用 for k,v in d.items()

字符串也是可迭代的对象,也可以用for循环

for ch in 'ABC':
  print(ch)
# A B C

那么如何判断一个对象是可迭代对象呢?方法是通过collections.abc模块的Iterable类判断

from collections.abc import Iterable
isinstance('abc',Iterable) #True
isinstance([1,2,3],Iterable)# True
isinstance(123,Iterable)# False

python内置的enumerate函数可以把一个list编程索引-元素对

for i,value in enumerate(['A','B','C']):
  print(i,value)
for x,y in [(1,1),(2,5),(3,9)]:
  print(x,y)

列表生成式

生成[1,4,9,…,81,100]怎么做呢

L=[]
for x in range(1,11):
  L.append(x)

改进成里列表生成式,用一行语句代替循环生成上面的list:

[x*x for x in range(1,11)]

写列表生成式时,把要生成的元素x*x 放到前面,后面跟for循环,就可以把list创建出来

for循环后面还可以加上if判断

[x*x for x in range(1:11) if x%2==0]
#[4,16,36,64,100]

还可以使用两层循环,生成全排列

[m+n for m in 'ABC' for n in 'XYZ']
import os
[d for d in os.listdir('.')] #os.listdir可以列出文件和目录
d={'x':'A','y':'B','z':'C'}
for k,v in d.items():
  print(k,'=',v)
  
[k+'='+v for k,v in d.ietems()]
L=['Hello','World','IBM','Apple']
[s.lower() for s in L]

生成列表相关if…else

[x for x in range(1,11) if x%2==0]

但是不能在if后面加上else

for后面的if是一个筛选条件

但是呢?把if下载for前面就必须加else,否则会报错

[x if x%2==0 else -x for x in range(1,11)]

这是因为for前面的部分是一个表达式,它必须根据x计算出一个结果,根据 x if x%2==0 无法根据x计算出结果。

总之:在一个列表生成中,for前面的if. else是表达式,而for后面的if是过滤条件,不能带else

生成器:

python中这种一边循环一边计算的机制,成为生成器,generator

创建generator的方法;

第一种:把列表生成表达式的[ ]改成( ),就创建了一个generator

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

可回忆通过next()函数获取generator的下一个返回值,直到计算到最后一个元素。

正确的方法是用for循环:

g=(x*x for x in range(10))
for n in g:
  print(n)

如果推算的算法比较复杂,用类似列表生成式的for循环无法实现的时候,还可以通过函数来实现

比如,著名的斐波那契数列

def fib(max):
  n,a,b=0,0,1
  while n 

fib函数实际上是定义了斐波那契数列的推算规则,可以从第一个元素开始,推算出后续人意的元素,这种逻辑类似generator

要把fib函数编程generator,只需要把print(b)改成yield b就可以了

def fib(max):
  n,a,b=0,0,1
  while n 

如果一个函数的定义中包含yield关键字,那么这个函数就是一个generator

循环过程中不断调用yield,就会不断终端,当然要给循环设置一个条件来退出循环,不然就会产生一个无线数列出来。

把函数改成generator后,基本上从来不会用next()来获取下一个返回值,而是用for循环来迭代

在for循环中不断计算出下一个元素,并在适当的条件结束for循环,对于函数改成的generator来说,遇到return语句或者执行到函数题最后一行语句,就是结束generator的指令,for循环随之结束

# -*- coding: utf-8 -*-

def triangles():
    L=[1,]
    while True:
        b=L.copy()
        yield b
        for i in range(1,len(b)):
            L[i]=b[i-1]+b[i]
        L.append(1)
#一下为测试用的调试代码
n = 0
results = []
for t in triangles():
    results.append(t)
    n = n + 1
    if n == 10:
        break

for t in results:
    print(t)

if results == [
    [1],
    [1, 1],
    [1, 2, 1],
    [1, 3, 3, 1],
    [1, 4, 6, 4, 1],
    [1, 5, 10, 10, 5, 1],
    [1, 6, 15, 20, 15, 6, 1],
    [1, 7, 21, 35, 35, 21, 7, 1],
    [1, 8, 28, 56, 70, 56, 28, 8, 1],
    [1, 9, 36, 84, 126, 126, 84, 36, 9, 1]
]:
    print('测试通过!')
else:
    print('测试失败!')

迭代器

可以直接作用于for循环的对象统称为可迭代对象,Iterable。可以使用isinstance()判断一个对象是否是Iterable对象

生成器不但可以用于for循环,还可以被next()函数不断调用并返回下一个值,直到抛出StopIteration错误表示无法继续返回下一个值了

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

>>> from collections.abc import Iterable
>>> isinstance([], Iterable)
True
>>> isinstance({}, Iterable)
True
>>> isinstance('abc', Iterable)
True
>>> isinstance((x for x in range(10)), Iterable)
True
>>> isinstance(100, Iterable)
False

>>> from collections.abc import Iterator
>>> isinstance((x for x in range(10)), Iterator)
True
>>> isinstance([], Iterator)
False
>>> isinstance({}, Iterator)
False
>>> isinstance('abc', Iterator)
False

List, dict,str等Iterable变成Iterator可以使用iter()

iter([])
iter('abc')

python的Iterator对象表示一个数据流,Iterator对象可以被next()函数调用并不断返回下一个数据,知道没有数据时抛出StopIteration错误,可以把这个数据流看成一个有序序列,但我们不能提前知道序列的长度,只能通过next()函数实现按需计算下一个数据,所以Iterator的计算是惰性的,只有在需要返回下一个数据时,它才会计算。

Iterator甚至可以表示一个无限大的数据流

凡是可作用于for循环的对象都是Iterable类型;

凡是可作用于next()函数的对象都是Iterator类型,它们表示一个惰性计算的序列;

集合数据类型如list、dict、str等是Iterable但不是Iterator,不过可以通过iter()函数获得一个Iterator对象。

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

高阶函数:

一个函数可以接收另一个函数作为参数,这种函数就称之为高阶函数

def add(x,y,f):
  return f(x)+f(y)

函数式编程就是指这种高度抽象的编程范式。

map()和reduce()函数

map加收两个参数,一个是函数,一个是Iterable

def f(x):
  return x*x
r=map(f,[1,2,3,4,5,6,7,8,9])
list(r)
# [1,4,9,16,25,36,49,64,81]

map传入的第一个参数是f,即函数对象本身,由于 r是一个Iterator,Iterator是让它把整个序列都计算出来并返回一个list

list(map(str,[1,2,3]))
# ['1','2','3']

reduce是把函数作用在一个序列[x1,x2,x3…]上,这个函数必须接收两个参数和序列的下一个元素做累积计算

reduce(f,[x1,x2,x4,x4])=f(f(f(x1,x2),x3),x4)
from functools import reduce
def add(x,y):
  return x+y
reduce(add,[1,3,5,7,9])

def fn(x,y):
  return 10*x+y
reduce(fn,[1,3,5,7,9])
#13579
from functools import reduce 
def fn(x, y):
    return x * 10 + y
def char2num(s):
    digits = {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}
    return digits[s]
reduce(fn, map(char2num, '13579'))
#13579

DIGITS = {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}
def str2int(s):
    def fn(x, y):
        return x * 10 + y
    def char2num(s):
        return DIGITS[s]
    return reduce(fn, map(char2num, s))

strip()函数,对用户的输入进行清理。

str.strip('序列‘)

其中,序列是一段字符串,该函数表示从头或者从尾部开始进行扫描,如果扫描的字符在序列字符串中,则剔除掉,一直到遇到一个不在序列字符串中的字符为止。

延伸的函数:

str.lstrip(‘序列’),则表示仅从头部第一个字符开始扫描,如果扫描的字符在序列字符串中,则剔除掉,直到遇到一个不在序列字符串中的字符为止。

str.rstrip(‘序列’),则表示仅从尾部第一个字符开始扫描,如果扫描的字符在序列字符串中,则剔除掉,直到遇到一个不在序列字符串中的字符为止。

特别的情况,如果函数圆括号里面的序列字符串为空,则默认为剔除首尾部的空白(包括空格、制表符),即strip()、lstrip()、rstrip().lambda只是一个表达式,函数体比def简单很多。

只能在lambda中封装有限的逻辑进去。

lambda表达式只是起到一个函数速写的作用

python内建的filter()用于过滤序列,和map类似,接收一个函数和一个序列,和map()不同的是,filter()把传入的函数依次作用于每个元素,然后根据返回值是True还是False决定保留还是丢弃该元素。

def _odd_iter():
    n = 1
    while True:
        n = n + 2
        yield n
        
def _not_divisible(n):
    return lambda x: x % n > 0
def primes():
    yield 2
    it = _odd_iter() # 初始序列
    while True:
        n = next(it) # 返回序列的第一个数
        yield n
        it = filter(_not_divisible(n), it) # 构造新序列 
# 打印1000以内的素数:
for n in primes():
    if n < 1000:
        print(n)
    else:
        break

sorted()函数

sorted函数可以接收一个key函数来实现自定义的大小排序

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

如果想要进行反向排序,不必改动key函数,可以传入第三个参数reverse=True

我是这样理解的,对list中的每个元素都用key的函数处理一下。然后按照从小到大排序,如果reverse=True,那么就从大到小排序。

函数作为返回值

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

def lazy_sum(*args):
    def sum():
        ax = 0
        for n in args:
            ax = ax + n
        return ax
    return sum

lazy_sum中定义了sum,并且sum可以引用外部局部函数lazy_sum的参数和局部变量,当lazy_sum返回函数sum时,相关参数和变量都保存在返回的函数中,这种称为闭包

这种称为闭包的程序结构具有极大的威力

f1 = lazy_sum(1, 3, 5, 7, 9)
f2 = lazy_sum(1, 3, 5, 7, 9)
f1==f2
# False

闭包牢记一点:返回函数不要引用任何循环变量,或者后续会发生变化的变量。

如果一定要引用循环变量怎么办?方法是再创建一个函数,用该函数的参数绑定循环变量当前的值,无论该循环变量后续如何更改,已绑定到函数参数的值不变:

def count():
    def f(j):
        def g():
            return j*j
        return g
    fs = []
    for i in range(1, 4):
        fs.append(f(i)) # f(i)立刻被执行,因此i的当前值被传入f()
    return fs

内部函数不能改变外部函数中变量的指向,但是用list可以避免这个问题,修改list中的值,并未改变list的指向。

还可以在内部函数中声明变量为nonlocal,让内部函数获得修改权

匿名函数

lambda函数,表示匿名函数,冒号前表示参数,并且不用写return

lambda x:x*x

可以把匿名函数赋值给另一个变量,也可以把匿名函数作为函数返回值

list(map(lambda x:x*x,[1,2,3,4,5,6,7,8,9]))

函数对象有一个__name__属性,可以拿到函数的名字

装饰器(先省略)https://www.liaoxuefeng.com/wiki/1016959663602400/1017451662295584

在代码运行期间,动态增加功能的方式,称之为装饰器,decorator

偏函数

当函数的参数个数太多,需要简化时,使用functools.partial可以创建一个新的函数,这个新函数可以固定住原函数的部分参数,从而在调用时更简单。

greeting()等 正常的函数和变量名是公开的,可以被直接引用。

类似__x__这样的变量是特殊变量,可以被直接引用,但是有特殊用途,自己的变量一般不要用这种变量名

_xx和__xx这样的函数或变量就是非公开的,private,不应该被直接引用。(并不等于不能引用)

如果要让内部属性不被外部访问,可以把属性的名称前加上两个下划线__,在Python中,实例的变量名如果以__开头,就变成了一个私有变量(private),只有内部可以访问,外部不能访问

在Python中,变量名类似__xxx__的,也就是以双下划线开头,并且以双下划线结尾的,是特殊变量,特殊变量是可以直接访问的,不是private变量,所以,不能用__name__、__score__这样的变量名。

下划线开头的实例变量是不是一定不能从外部访问呢?其实也不是。不能直接访问__name是因为Python解释器对外把__name变量改成了_Student__name,所以,仍然可以通过_Student__name来访问__name变量

python的类中__xx变量会自动变成 _类名__xx

(不要用赋值语句,赋值语句会给实例增加一个新的属性)

47.继承和多态

class Animal(object):
  def run(self):
    print("running!")
class Dog(Animal):
  pass
class Cat(Animal):
  pass

继承,最大的好处就是子类获得了父类的全部功能

当子类和父类都存在相同的run()方法时,子类的run()覆盖类父类的run(),在代码运行的时候,总会调用子类run(),获得了继承的另一个好处:多态

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

a=list()
isinstance(a,list)
#True

静态语言vs动态语言

对于静态语言(Java),如果需要传入Animal类型,则传入的对象必须是Animal类型或者它的子类,否则,将无法调用run()方法。

对于python这样的动态语言来说,则不一定需要传入Animal类型,我们只需要保证传入的对象有一饿run()方法就可以了

class Timer(object):
  def run(self):
    print('start')

如何知道这个对象是什么类型、有哪些方法呢?

使用type()

type(123)
type('str')
type(None)

判断函数的类型

import types
def fn():
  pass
type(fn)==types.FunctionType
type(abs)==types.BuiltinFunctionType
type(lambda x:x)==types.LambdaType
type((x for x in range(10)))==type.GeneratorType

对于继承关系来说,使用type就很不方便,我们要判断class的类型,可以使用isinstance()函数

能用type()判断的基本类型也可以用isinstance()判断

并且还能够判断一个变量是否是某些类型中的一种

isinstance([1,2,3],(list,tuple))

总是优先使用isinstance()判断类型

dir()方法,可以获得一个对象的所有属性和方法,它返回一个包含字符串的list

类似于__xx__的属性和方法在python中都是有特殊用途的,比如__len__方法返回长度,调用len()函数,实际上内部就是自动去调用该对象的__len__()方法

len('ABC')
'ABC'.__len__()#两种说法等价

getattr() setattr() hasattr()可以直接操作一个对象的状态

hasattr(obj,'x')#有属性'x'吗?
setattr(obj,'y',19)#设置一个属性'y'
getattr(obj,'y')#获取属性'y'

如果试图获取不存在的属性,会抛出AttributeError的错误

可以传入一个default参数,如果属性不存在,就返回默认值

getattr(obj,'z',404)#不存在返回404

示例属性属于各个实例所有,互不干扰;

类属性属于类所有,所有实例共享一个属性;

不要对实例属性和类属性使用相同的名字,否则将产生难以发现的错误

def set_score(self, score):
    self.score = score
Student.set_score = set_score

def set_age(self, age): # 定义一个函数作为实例方法
    self.age = age
from types import MethodType
s.set_age = MethodType(set_age, s) # 给实例绑定一个方法(必须MethodType)
s.set_age(25) # 调用实例方法

使用__slots__

如果要限制实例的属性,就要在定义类的时候,定义一个特殊的__slots__变量,来限制class实例能添加的属性:

class Student(object):
  __slots__=('name','age')

使用__slots__时要注意,__slots__定义的属性仅对当前类实例起作用,对继承的子类是不起作用的

除非在子类中也定义__slots__,这样,子类实例允许定义的属性就是自身的__slots__加上父类的__slots__

@property-装饰器

搞不清楚

https://www.liaoxuefeng.com/wiki/1016959663602400/1017502538658208

多重继承

class Dog(Mammal,Flyable):
  pass

MixIn的目的就是给一个类多个功能,这样,在设计类的时候,我们有限考虑通过多重继承来组合多个MixIn的功能,而不是设计多层次的复杂的继承关系。

class Dog(Mammal,RunnableMixIn,CarnivorousMixIn):
  pass

__str__()方法,返回一个字符串

class Student(object):
  def __init__(self,name):
    self.name=name
  def __str__(self):
    return 'Student object (name:%s)'%self.name
print(Student('Michael'))

__str__()是返回对象看得到的字符,__repr__()返回程序开发者看到的字符,为调试服务

class Student(object):
    def __init__(self, name):
        self.name = name
    def __str__(self):
        return 'Student object (name=%s)' % self.name
    __repr__ = __str__

__iter__()

__getitem__()

__getattr__()

__call__()

https://www.liaoxuefeng.com/wiki/1016959663602400/1017590712115904#0定制类

https://www.liaoxuefeng.com/wiki/1016959663602400/1017595944503424枚举类

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/300585.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号