您的位置:首页 > 编程语言 > Python开发

Python特殊用法

2019-06-01 16:33 169 查看

1.如果要取最后一个元素,除了计算索引位置外,还可以用-1做索引,直接获取最后一个元素:
classmates = [‘Michael’, ‘Bob’, ‘Tracy’]

classmates[-1]
‘Tracy’
以此类推,可以获取倒数第2个、倒数第3个:

classmates[-2]
‘Bob’

classmates[-3]
2.另一种有序列表叫元组:tuple。tuple和list非常类似,但是tuple一旦初始化就不能修改
3.要定义一个只有1个元素的tuple,如果你这么定义:

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

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

t = (1,)
t
(1,)
Python在显示只有1个元素的tuple时,也会加一个逗号,,以免你误解成数学计算意义上的括号。
4.最后来看一个“可变的”tuple:

t = (‘a’, ‘b’, [‘A’, ‘B’])
t[2][0] = ‘X’
t[2][1] = ‘Y’
t
(‘a’, ‘b’, [‘X’, ‘Y’])
5.表面上看,tuple的元素确实变了,但其实变的不是tuple的元素,而是list的元素。tuple一开始指向的list并没有改成别的list,所以,tuple所谓的“不变”是说,tuple的每个元素,指向永远不变。即指向’a’,就不能改成指向’b’,指向一个list,就不能改成指向其他对象,但指向的这个list本身是可变的!

理解了“指向不变”后,要创建一个内容也不变的tuple怎么做?那就必须保证tuple的每一个元素本身也不能变。
6.if判断条件还可以简写,比如写:

if x:
print ‘True’
只要x是非零数值、非空字符串、非空list等,就判断为True,否则为False。
7.计算100以内所有奇数之和,可以用while循环实现:

sum = 0
n = 99
while n > 0:
sum = sum + n
n = n - 2
print sum
8.函数名其实就是指向一个函数对象的引用,完全可以把函数名赋给一个变量,相当于给这个函数起了一个“别名”:

a = abs # 变量a指向abs函数
a(-1) # 所以也可以通过a调用abs函数
1
9.当函数有多个参数时,把变化大的参数放前面,变化小的参数放后面。变化小的参数就可以作为默认参数。,默认参数就是具有固定值得参数

使用默认参数最大的好处是能降低调用函数的难度。
def enroll(name, gender, age=6, city=‘Beijing’):
print(‘name:’, name)
print(‘gender:’, gender)
print(‘age:’, age)
print(‘city:’, city)
大多数学生注册时不需要提供年龄和城市,只提供必须的两个参数:

enroll(‘Sarah’, ‘F’)
name: Sarah
gender: F
age: 6
city: Beijing
10.定义默认参数要牢记一点:默认参数必须指向不变对象!
11.def add_end(L=[]):
L.append(‘END’)
return L
当你正常调用时,结果似乎不错:

add_end([1, 2, 3])
[1, 2, 3, ‘END’]

add_end([‘x’, ‘y’, ‘z’])
[‘x’, ‘y’, ‘z’, ‘END’]
当你使用默认参数调用时,一开始结果也是对的:

add_end()
[‘END’]
再次调用add_end()时,结果就不对了:

add_end()
[‘END’, ‘END’]

add_end()
[‘END’, ‘END’, ‘END’]
很多初学者很疑惑,默认参数是[],但是函数似乎每次都“记住了”上次添加了’END’后的list

12.用None这个不变对象来实现:

def add_end(L=None):
if L is None:
L = []
L.append(‘END’)
return L
现在,无论调用多少次,都不会有问题:

add_end()
[‘END’]

add_end()
[‘END’]

设计str、None这样的不变对象是因为不变对象一旦创建,对象内部的数据就不能修改,这样就减少了由于修改数据导致的错误。此外,由于对象不变,多任务环境下同时读取对象不需要加锁,同时读一点问题都没有。我们在编写程序时,如果可以设计一个不变对象,那就尽量设计成不变对象。
13.Python允许你在list或tuple前面加一个*号,把list或tuple的元素变成可变参数传进去:

nums = [1, 2, 3]
calc(*nums)
14.可变参数允许你传入0个或任意个参数,这些可变参数在函数调用时自动组装为一个tuple。而关键字参数允许你传入0个或任意个含参数名的参数,这些关键字参数在函数内部自动组装为一个dict。
def person(name, age, **kw):
print(‘name:’, name, ‘age:’, age, ‘other:’, kw)
函数person除了必选参数name和age外,还接受关键字参数kw。在调用该函数时,可以只传入必选参数:

person(‘Michael’, 30)
name: Michael age: 30 other: {}
也可以传入任意个数的关键字参数:

person(‘Bob’, 35, city=‘Beijing’)
name: Bob age: 35 other: {‘city’: ‘Beijing’}

person(‘Adam’, 45, gender=‘M’, job=‘Engineer’)
name: Adam age: 45 other: {‘gender’: ‘M’, ‘job’: ‘Engineer’}
15.和可变参数类似,也可以先组装出一个dict,然后,把该dict转换为关键字参数传进去:

extra = {‘city’: ‘Beijing’, ‘job’: ‘Engineer’}
person(‘Jack’, 24, city=extra[‘city’], job=extra[‘job’])
name: Jack age: 24 other: {‘city’: ‘Beijing’, ‘job’: ‘Engineer’}
当然,上面复杂的调用可以用简化的写法:

extra = {‘city’: ‘Beijing’, ‘job’: ‘Engineer’}
person(‘Jack’, 24, **extra)
name: Jack age: 24 other: {‘city’: ‘Beijing’, ‘job’: ‘Engineer’}
16.extra表示把extra这个dict的所有key-value用关键字参数传入到函数的kw参数,kw将获得一个dict,注意kw获得的dict是extra的一份拷贝,对kw的改动不会影响到函数外的extra
17.关键字参数,函数的调用者可以传入任意不受限制的关键字参数
18.如果要限制关键字参数的名字,就可以用命名关键字参数,命名关键字参数,只接收city和job作为关键字参数。这种方式定义的函数如下:

def person(name, age, , city, job):
print(name, age, city, job)
和关键字参数**kw不同,命名关键字参数需要一个特殊分隔符,*后面的参数被视为命名关键字参数。

调用方式如下:

person(‘Jack’, 24, city=‘Beijing’, job=‘Engineer’)
Jack 24 B 4000 eijing Engineer
19.如果函数定义中已经有了一个可变参数,后面跟着的命名关键字参数就不再需要一个特殊分隔符*了:

def person(name, age, args, city, job):
print(name, age, args, city, job)
20.在Python中定义函数,可以用必选参数、默认参数、可变参数、关键字参数和命名关键字参数,这5种参数都可以组合使用。但是请注意,参数定义的顺序必须是:必选参数、默认参数、可变参数(tuple)、命名关键字参数和关键字参数(dict)
21.默认参数一定要用不可变对象
要注意定义可变参数和关键字参数的语法:
args是可变参数,args接收的是一个tuple;
**kw是关键字参数,kw接收的是一个dict。
调用函数时如何传入可变参数和关键字参数的语法:
可变参数既可以直接传入:func(1, 2, 3),又可以先组装list或tuple,再通过args传入:func((1, 2, 3));
关键字参数既可以直接传入:func(a=1, b=2),又可以先组装dict,再通过kw传入:func({‘a’: 1, ‘b’: 2})

使用*args和**kw是Python的习惯写法,当然也可以用其他参数名,但最好使用习惯用法。
命名的关键字参数是为了限制调用者可以传入的参数名,同时可以提供默认值。

定义命名的关键字参数在没有可变参数的情况下不要忘了写分隔符*,否则定义的将是位置参数
22.在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出
23.解决递归调用栈溢出的方法是通过尾递归优化,事实上尾递归和循环的效果是一样的,所以,把循环看成是一种特殊的尾递归函数也是可以的。

尾递归是指,在函数返回的时候,调用自身本身,并且,return语句不能包含表达式。这样,编译器或者解释器就可以把尾递归做优化,使递归本身无论调用多少次,都只占用一个栈帧,不会出现栈溢出的情况。
23.所有的递归函数都可以写成循环的方式,但循环的逻辑不如递归清晰,尾递归方式,需要多一点代码,主要是要把每一步的乘积传入到递归函数中:

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 fact_iter(num - 1, num * product)仅返回递归函数本身,num - 1和num * product在函数调用前就会被计算,不影响函数调用。

fact(5)对应的fact_iter(5, 1)的调用如下:

===> fact_iter(5, 1)
===> fact_iter(4, 5)
===> fact_iter(3, 20)
===> fact_iter(2, 60)
===> fact_iter(1, 120)
===> 120
尾递归调用时,如果做了优化,栈不会增长,因此,无论多少次调用也不会导致栈溢出。
24.使用递归函数的优点是逻辑简单清晰,缺点是过深的调用会导致栈溢出。

针对尾递归优化的语言可以通过尾递归防止栈溢出。尾递归事实上和循环是等价的,没有循环语句的编程语言只能通过尾递归实现循环。

Python标准的解释器没有针对尾递归做优化,任何递归函数都存在栈溢出的问题
25.汉诺塔:有三个柱子,把第一个柱子上的所有东西移动到第三个柱子上,移动过程中也必须是从小到大,最后第三个柱子上的东西也是从小到大排列
26.切片

L = list(range(100))
L
[0, 1, 2, 3, …, 99]

L[:10]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
L[:10:2]
[0, 2, 4, 6, 8]
甚至什么都不写,只写[:]就可以原样复制一个list:

L[:]
[0, 1, 2, 3, …, 99]
‘ABCDEFG’[::2]
‘ACEG’

tuple和字符串也可以切片,切片后还是tuple和字符串

在很多编程语言中,针对字符串提供了很多各种截取函数(例如,substring),其实目的就是对字符串切片

有了切片操作,很多地方循环就不再需要了。Python的切片非常灵活,一行代码就可以实现很多行循环才能完成的操作
27.迭代
在Python中,迭代是通过for … in来完成的
列表、tuple、dict 、字符串、generator都是可迭代对象

d = {‘a’: 1, ‘b’: 2, ‘c’: 3}
for key in d:
… print(key)

a
c
b
如果要迭代value,可以用for value in d.values(),如果要同时迭代key和value,可以用for k, v in d.items()

当我们使用for循环时,只要作用于一个可迭代对象,for循环就可以正常运行,而我们不太关心该对象究竟是list还是其他数据类型。

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

from collections import Iterable
isinstance(‘abc’, Iterable) # str是否可迭代
True

isinstance([1,2,3], Iterable) # list是否可迭代
True

isinstance(123, Iterable) # 整数是否可迭代
False
Python内置的enumerate函数可以把一个list变成索引-元素对,这样就可以在for循环中同时迭代索引和元素本身:

for i, value in enumerate([‘A’, ‘B’, ‘C’]):
… print(i, value)

0 A
1 B
2 C
for x, y in [(1, 1), (2, 4), (3, 9)]:
… print(x, y)

1 1
2 4
3 9
28.列表生成式
Python内置的非常简单却强大的可以用来创建list的生成式
[x * x for x in range(1, 11)]
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

[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’]
[‘AX’, ‘AY’, ‘AZ’, ‘BX’, ‘BY’, ‘BZ’, ‘CX’, ‘CY’, ‘CZ’]

运用列表生成式,可以写出非常简洁的代码。例如,列出当前目录下的所有文件和目录名,可以通过一行代码实现:

import os # 导入os模块
[d for d in os.listdir(’.’)] # os.listdir可以列出文件和目录
[’.emacs.d’, ‘.ssh’, ‘.Trash’, ‘Adlm’, ‘Applications’, ‘Desktop’, ‘Documents’, ‘Downloads’, ‘Library’, ‘Movies’, ‘Music’, ‘Pictures’, ‘Public’, ‘VirtualBox VMs’, ‘Workspace’, ‘XCode’]

for循环其实可以同时使用两个甚至多个变量,比如dict的items()可以同时迭代key和value:

d = {‘x’: ‘A’, ‘y’: ‘B’, ‘z’: ‘C’ }
for k, v in d.items():
… print(k, ‘=’, v)

y = B
x = A
z = C

列表生成式也可以使用两个变量来生成list:

d = {‘x’: ‘A’, ‘y’: ‘B’, ‘z’: ‘C’ }
[k + ‘=’ + v for k, v in d.items()]
[‘x=A’, ‘y=B’, ‘z=C’]

把一个list中所有的字符串变成小写:

L = [‘Hello’, ‘World’, ‘IBM’, ‘Apple’]
[s.lower() for s in L]
[‘hello’, ‘world’, ‘ibm’, ‘apple’]
29.在Python中,这种一边循环一边计算的机制,称为生成器:generator。
要创建一个generator,有很多种方法。第一种方法很简单,只要把一个列表生成式的[]改成(),就创建了一个generator
可以通过next()函数获得generator的下一个返回值

使用for循环,因为generator也是可迭代对象:

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

0
1
4
9
16
25
36
49
64
81

如果一个函数定义中包含yield关键字,那么这个函数就不再是一个普通函数,而是一个generator

函数是顺序执行,遇到return语句或者最后一行函数语句就返回。而变成generator的函数,在每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行

定义一个generator,依次返回数字1,3,5:

def odd():
print(‘step 1’)
yield 1
print(‘step 2’)
yield(3)
print(‘step 3’)
yield(5)
调用该generator时,首先要生成一个generator对象,然后用next()函数不断获得下一个返回值:

o = odd()
next(o)
step 1
1

next(o)
step 2
3

next(o)
step 3
5

next(o)
Traceback (most recent call last):
File “”, line 1, in
StopIteration

用for循环调用generator时,发现拿不到generator的return语句的返回值。如果想要拿到返回值,必须捕获StopIteration错误,返回值包含在StopIteration的value中:

g = fib(6)
while True:
… try:
… x = next(g)
… print(‘g:’, x)
… except StopIteration as e:
… print(‘Generator return value:’, e.value)
… break

g: 1
g: 1
g: 2
g: 3
g: 5
g: 8
Generator return value: done

普通函数和generator函数,普通函数调用直接返回结果:

r = abs(6)
r
6
generator函数的“调用”实际返回一个generator对象:

g = fib(6)
g
<generator object fib at 0x1022ef948>
29.高阶函数
函数就是面向过程的程序设计的基本单元
函数式编程的一个特点就是,允许把函数本身作为参数传入另一个函数,还允许返回一个函数!
函数本身也可以赋值给变量,即:变量可以指向函数

f = abs
f(-10)
10
成功!说明变量f现在已经指向了abs函数本身。直接调用abs()函数和调用变量f()完全相同。
变量可以指向函数,函数的参数能接收变量,那么一个函数就可以接收另一个函数作为参数,这种函数就称之为高阶函数。

一个最简单的高阶函数:

def add(x, y, f):
return f(x) + f(y)
当我们调用add(-5, 6, abs)时,参数x,y和f分别接收-5,6和abs,根据函数定义,我们可以推导计算过程为:

x = -5
y = 6
f = abs
f(x) + f(y) ==> abs(-5) + abs(6) ==> 11
return 11
30.
map()函数接收两个参数,一个是函数,一个是Iterable,map将传入的函数依次作用到序列的每个元素,并把结果作为新的Iterator返回。

def f(x):
… return x * x

r = map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9])
list®
[1, 4, 9, 16, 25, 36, 49, 64, 81]
reduce把一个函数作用在一个序列[x1, x2, x3, …]上,这个函数必须接收两个参数,reduce把结果继续和序列的下一个元素做累积计算,其效果就是:

reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)
利用高阶函数reduce()把str转换为int的函数

from functools import reduce
def fn(x, y):
… return x * 10 + y

reduce(fn, [1, 3, 5, 7, 9])
13579
(((1*10+3)*10+5)*10+7)*10+9=13579

from functools import reduce

DIGITS = {‘0’: 0, ‘1’: 1, ‘2’: 2, ‘3’: 3, ‘4’: 4, ‘5’: 5, ‘6’: 6, ‘7’: 7, ‘8’: 8, ‘9’: 9}

def char2num(s):
return DIGITS[s]

def str2int(s):
return reduce(lambda x, y: x * 10 + y, map(char2num, s))—lambda匿名函数,参数是x,y,函数体是x*10+y,初始值 map(char2num, s)
31.
Python内建的filter()函数用于过滤序列。
和map()类似,filter()也接收一个函数和一个序列。
32.
sorted()函数也是一个高阶函数,它还可以接收一个key函数来实现自定义的排序,例如按绝对值大小排序:

sorted([36, 5, -12, 9, -21], key=abs)
[5, 9, -12, -21, 36]
33.函数作为返回值
高阶函数除了可以接受函数作为参数外,还可以把函数作为结果值返回
每次调用都会返回一个新的函数,即使传入相同的参数:

f1 = lazy_sum(1, 3, 5, 7, 9)
f2 = lazy_sum(1, 3, 5, 7, 9)
f1==f2
False
f1()和f2()的调用结果互不影响。
34.返回闭包时牢记一点:返回函数不要引用任何循环变量,或者后续会发生变化的变量
如果在一个内部函数里对外部作用域(但不是全局作用域)的变量进行引用,内部函数称为闭包(closure)
返回一个函数时,牢记该函数并未执行,返回函数中不要引用任何可能会变化的变量

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: