python不容易注意的知识点_Python中容易混淆的知识点

2ff34e647e2e3cdfd8dca593e17d9b0a.png

函数

匿名函数

匿名函数就是不需要指定函数的名字,比如lambda x: x * x。相当与:1

2

3

4

5

6def (x):

return x * x

def (x, y):

return lambda: x * x + y * y

函数参数

可变参数:可以接受所有参数1

2

3

4

5

6

7

8

9

10

11

12

13

14def calc(*numbers):

sum = 0

for n in numbers:

sum = sum + n * n

return sum

calc(1, 2, 3)

14

nums = [1, 2, 3]

>>>calc(*nums)

14

关键字参数:使用**来定义,允许传入0个或者任意个含参数名的参数,在内部自动组装成dict1

2

3

4

5def person(name, age, **kw):

print('name:', name, 'age:', age, 'other:', kw)

>>>person('Jim', 18, city='Beijing')

name: Jim age: 18 other: {'city': 'Beijing'}

命名关键字参数:可以限制关键字参数的名字,比如只接受某个参数名为关键字参数。命名关键字参数需要用*进行分隔开,*后面的参数被视为关键字参数。1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32def person(name, age, *, city, job):

print(name, age, city, job)

>>>person('Jack', 24, city='Beijing', job='Engineer')

Jack 24 Beijing Engineer

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

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

print(name, age, args, city, job)

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

>>>person('Jack', 24, 'Beijing', 'Engineer')

Traceback (most recent call last):

File "", line 1, in

TypeError: person() takes 2 positional arguments but 4 were given

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

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

print(name, age, city, job)

# 由于命名关键字参数city具有默认值,调用时,可不传入city参数:

>>>person('Jack', 24, job='Engineer')

Jack 24 Beijing Engineer

# 使用命名关键字参数时,要特别注意,如果没有可变参数,就必须加一个'*'作为特殊分隔符。如果缺少*,Python解释器将无法识别位置参数和命名关键字参数:

def person(name, age, city, job):

# 缺少 *,city和job被视为位置参数

pass

递归函数:

在函数内部,可以调用其他函数,如果一个函数在内部调用自身,那么就称作递归函数。1

2

3

4

5

6

7

8

9

10

11

12

13

14

15def fact(n):

if n==1:

return 1

return n * fact(n - 1)

===> fact(5)

===> 5 * fact(4)

===> 5 * (4 * fact(3))

===> 5 * (4 * (3 * fact(2)))

===> 5 * (4 * (3 * (2 * fact(1))))

===> 5 * (4 * (3 * (2 * 1)))

===> 5 * (4 * (3 * 2))

===> 5 * (4 * 6)

===> 5 * 24

===> 120

递归函数的优点:逻辑清晰,但是使用尾递归要注意防止栈溢出。(函数是通过栈这种数据结构来实现的。所以递归调用的次数越多,会导致栈溢出) 解决这个办法是通过尾递归优化。

尾递归优化:当函数返回的时候,调用自身本身,并且return语句不能包含表达式。这样编译器或者就是去就可以把尾递归做优化,使得尾递归不论本身调用多少次,都只占用了一个栈。不会出现栈溢出的情况。上面的例子return n * fact(n - 1)不是一个尾递归。

一个尾递归的例子:1

2

3

4

5

6

7

8# 这个函数是一个递归调用,所以并不会导致栈溢出。但是由于Python解释器并没有做尾递归优化,所以仍然会导致栈溢出。

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)

列表生成式:

如果想要生成一个list,可以使用list(range(1, 11)),也可使用for循环,然而使用列表生成式可以很方便的生成list:把要生成的元素x * x放到前面,后面跟着for循环,就可以把list创建出来1[x * x for x in range(1, 11)]

后面还可以跟if语句,求偶数的平方:1

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

[4, 16, 36, 64, 100]

使用两层循环,使用两个变量来生成list:1

2

3

4

5

6>>>[m + n for m in 'ABC' for n in 'XYZ']

['AX', 'AY', 'AZ', 'BX', 'BY', 'BZ', 'CX', 'CY', 'CZ']

>>>d = {'x': 'A', 'y': 'B', 'z': 'C' }

>>>[k + '=' + v for k, v in d.items()]

['y=B', 'x=A', 'z=C']

生成器与迭代器

生成器:

Python中,一边循环一边计算的机制,称为生成器(generator),只要把一个列表生成式的[]改成(),就创建了generator。

有两种方式创建生成器:1

2

3

4

5

6

7

8

9# 列表生成式

[x * x for x in range(10)]

# generator

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

# 每次调用 next(g),就可以计算出g的下一个元素的值,直到最后抛出 StopIteration 错误。由于generator是可迭代对象。所以可以用for循环来遍历。

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

for n in g:

print(n)

第二种方法创建生成器,如果函数定义中包含yield关键字,那么这个函数就是一个生成器。在generator当中,每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行,举个简单的例子:1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22def odd():

print('step 1')

yield 1

print('step 2')

yield(3)

print('step 3')

yield(5)

>>>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时,如果想要拿到返回值,必须捕获错误,返回值包含在StopIteration的value中:1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16>>>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

###迭代器:

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

2

3

4from collections import Iterable

isinstance([], Iterable)

isinstance({}, Iterable)

isinstance(100, Iterable) // False

生成器不但可以作用于 for 循环,而且可以被 next() 函数不断调用返回下一个值,直到最后抛出StopIteration错误,这样可以被next()函数不断返回下一个值得对象称为迭代器:Iterator。

生成器都是Iterator对象,但是list、dict、str是可迭代对象(Iterable),但是不是迭代器Iterator。需要把以上类型变成可迭代对象可以使用 iter() 函数。1

2isinstance(iter([]), Iterator)

// True

list、dict、str 等数据不是Iterator的原因是:Python 中的Iterator对象表示的是一个数据流,Iterator对象可以被next()函数不断调用返回下一个数据,直到没有数据时抛出StopIterator错误。可以把这个数据流看做是一个有序的序列,但我们却不能提前直到这个序列的长度,只能不断的调用next()函数事项按需计算下一个数据,所以Iterator的计算是惰性的,只有在需要返回下一个数据的时候才会计算,Iterator甚至可以表示出一个无限大的数据流,例如全体自然数,而使用list是完全不可能存储全体自然数的。

总结:

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

凡是可以作用于 next()函数的对象都是 Iterator类型,表示一种惰性计算。

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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值