跟着廖雪峰老师学python (2)

若想技术精进,当然得把基础知识打得牢牢的。

廖雪峰的官方网站  python3教程,该网站提供的教程浅显易懂,还附带了讲学视频,非常适合初学者正规入门。

以下是通过廖雪峰python官方网站学习的个人查漏补缺。

主要内容包括:1.切片  2.迭代  3.列表生成式  4.生成器  5.迭代器  6.函数式编程  7.map/reduce  8.filter  9.sorted  10.返回函数  11.匿名函数  12.装饰器  13.偏函数

1. 切片

切片:Python的切片非常灵活,一行代码就可以实现很多行循环才能完成的操作。  

>>> L = ['Michael','Sarah','Tracy','Bob','Jack']     

>>> L[0:3]

['Michael', 'Sarah', 'Tracy']

>>> L[:3]

['Michael', 'Sarah', 'Tracy']

L[0:3]表示,从索引0开始取,直到索引3为止,但不包括索引3。即索引0,1,2,正好是3个元素。如果第一个索引是0,还可以省略:L[:3]。


>>> L = list(range(100))

>>> L[-10:]   ##取后10个

[90, 91, 92, 93, 94, 95, 96, 97, 98, 99]

>>> L[:10:2]   ##前10个数,每两个取一个

[0, 2, 4, 6, 8]

>>> L[::5]   ##所有数,每5个取一个

[0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95]

tuple也是一种list,唯一区别是tuple不可变。因此,tuple也可以用切片操作,只是操作的结果仍是tuple。

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


2.迭代

很多语言比如C语言,迭代list是通过下标完成的,比如Java代码:

for (i=0; i<list.length; i++) {

    n=list[i];

}

Python对list列表和tuple元组迭代通过  for ... in 来完成的。   注:list这种数据类型虽然有下标,但很多其他数据类型是没有下标的,但是,只要是可迭代对象,无论有无下标,都可以迭代,比如dict就可以迭代。

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

>>> from collections import Iterable

>>> isinstance('abc', Iterable) # str是否可迭代

True

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

True

>>> isinstance(123, Iterable) # 整数是否可迭代

False

判断是不是可迭代对象方法

Python对dict字典的迭代,默认情况下迭代的是key:  for key in dictionary 来完成。  如果要迭代value,可以使用 for value in dictinary.values() 来完成。  如果要同时迭代key和vaule,可以使用  for k, v in dictionary.items() 来完成。

d={'a':1, 'b':2, 'c':3}

for key in d:  #默认情况下用key迭代

for value in d.values():  #使用value来迭代

for k, v in d.items():  #使用key和vaule同时迭代

如果要对list实现类似Java那样的下标循环怎么办?Python内置的enumerate函数可以把一个list变成索引-元素对,这样就可以在for循环中同时迭代索引和元素本身:

for循环里,同时引用了两个变量,在Python里是很常见的


3.列表生成式

列表生成式:列表生成式即List Comprehensions,是Python内置的非常简单却强大的可以用来创建list的生成式。

####生成 list:[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

>>>list(range(1,11))

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

####生成 list :[1x1, 2x2, 3x3, ..., 10x10]

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

[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

####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']

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

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

>>> import os # 导入os模块

>>> [d for d in os.listdir('.')]     # os.listdir可以列出文件和目录

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

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

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

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

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

>>> L = ['Hello', 'World', 'IBM', 'Apple']

>>> [s.lower() for s in L]

['hello', 'world', 'ibm', 'apple']

运用列表生成式,可以快速生成list,可以通过一个list推导出另一个list,而代码却十分简洁。


4.生成器

为什么是生成器???——通过列表生成式,我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。

所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器:generator

要创建一个generator,有很多种方法。第一种方法很简单,只要把一个列表生成式的[ ]改成( ),就创建了一个generator:

>>> L = [x * x for x in range(10)]   ## [ ] 列表生成式

>>> L

[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

>>> g = (x * x for x in range(10))  ##( ) 生成器

>>> g

<generator object <genexpr> at 0x1022ef630>

####如果要一个一个打印出来,可以通过next()函数获得generator的下一个返回值:

>>> next(g)

0

####生成器generator也是可迭代对象,使用for循环迭代

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

>>> for n in g:

...     print(n)

... 

我们创建了一个generator后,基本上永远不会调用next(),而是通过for循环来迭代它,并且不需要关心StopIteration的错误。generator非常强大。如果推算的算法比较复杂,用类似列表生成式的for循环无法实现的时候,还可以用函数来实现。

斐波拉契数列用列表生成式写不出来,但是,用函数把它打印出来却很容易:

斐波拉契数列,函数实现的方式

fib函数实际上是定义了斐波拉契数列的推算规则,可以从第一个元素开始,推算出后续任意的元素,这种逻辑其实非常类似generator。也就是说,上面的函数和generator仅一步之遥。要把fib函数变成generator,只需要把print(b)改为yield b就可以了:

函数fib()和生成器fib()执行流程不一样

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


5.迭代器

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

一类是集合数据类型,如list列表、tuple元组、dict字典、set集合、str字符串等;

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

这些可以直接作用于for循环的对象统称为可迭代对象Iterable

注:生成器都是Iterator对象,但list、dict、str虽然是Iterable,却不是Iterator。

把list、dict、str等Iterable变成Iterator可以使用iter()函数

>>> isinstance(iter([]), Iterator)

True

>>> isinstance(iter('abc'), Iterator)

True

为什么list、dict、str等数据类型不是Iterator迭代器对象?

这是因为Python的Iterator对象表示的是一个数据流,Iterator对象可以被next()函数调用并不断返回下一个数据,直到没有数据时抛出StopIteration错误。可以把这个数据流看做是一个有序序列,但我们却不能提前知道序列的长度,只能不断通过next()函数实现按需计算下一个数据,所以Iterator的计算是惰性的,只有在需要返回下一个数据时它才会计算。Iterator甚至可以表示一个无限大的数据流,例如全体自然数。而使用list是永远不可能存储全体自然数的。

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

小结

1)切片:list、tuple、str都可以切片,且切片操作非常灵活、代码简单。

2)迭代:使用for...in xxx: 语句, list、tuple、set、dict、str都是可迭代的。凡是可作用于for循环的对象都是Iterable类型。

3)列表生成式:使用简单语句生成列表[ ]

4)生成器:最简单的方法,将列表生成式[ ] 改为( ) 就是一个生成器;  将函数改为生成器函数,使用yield,每调用一次都会在这暂时停留一下,你不再调用,我就不再走。一般使用for循环遍历。

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




6.函数式编程

函数式编程就是一种抽象程度很高的编程范式,纯粹的函数式编程语言编写的函数没有变量,因此,任意一个函数,只要输入是确定的,输出就是确定的,这种纯函数我们称之为没有副作用。而允许使用变量的程序设计语言,由于函数内部的变量状态不确定,同样的输入,可能得到不同的输出,因此,这种函数是有副作用的。

函数式编程的一个特点就是,允许把函数本身作为参数传入另一个函数,还允许返回一个函数!

1)变量可以指向函数

>>>f=abs  ###变量f,指向函数abs

>>>f(-10)###直接调用abs()函数和调用变量f()完全相同

10

2)函数名也是变量

>>> abs = 10

>>> abs(-10)    ###abs已经被更改为整数,其函数功能就不能用了

Traceback (most recent call last):

    File "<stdin>", line 1, in <module>

TypeError: 'int' object is not callable

3)传入函数

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

def add(x, y, f):

    return f(x) + f(y)

x = -5

y = 6

f = absf(x) + f(y) ==> abs(-5) + abs(6) ==> 11

把函数作为参数传入,这样的函数称为高阶函数,函数式编程就是指这种高度抽象的编程范式。


7.map/reduce

Python内置map()和reduce()函数。

map()高阶函数接收两个参数,一个是函数,一个是Iterable,map将传入的函数依次作用到序列的每个元素(map函数本质,函数一一作用于元素),并把结果作为新的Iterator返回。

>>> def f(x):

...      return x * x

...

>>> r = map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9])  #map(函数名,可迭代对象) 函数一一作用于可迭代对象中的元素,最后返回一个迭代器:Iterator惰性序列    r。

>>> list(r)    ###Iterator迭代器(惰性序列),使用list()将整个序列都计算出来,并返回一个list。

[1, 4, 9, 16, 25, 36, 49, 64, 81]

reduce()把一个函数作用在一个序列[x1, x2, x3, ...]上,这个函数必须接收两个参数,reduce把结果继续和序列的下一个元素做累积计算(reduce函数本质,函数把结果再代入充当其中的一个参数),其效果就是:

reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)

###reduce例子

>>> from functools import reduce

>>> def fn(x, y):

...      return x * 10 + y

...

>>> reduce(fn, [1, 3, 5, 7, 9])

13579


8.filter

Python内置filter()函数用于过滤序列。

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

def is_odd(n):

    return n % 2 == 1

list(filter(is_odd, [1, 2, 4, 5, 6, 9, 10, 15]))

# 结果: [1, 5, 9, 15]

可见用filter()这个高阶函数,关键在于正确实现一个“筛选”函数,即能够返回True/False的函数。filter()函数返回的是一个Iterator迭代器,也就是一个惰性序列,所以要强迫filter()完成计算结果,需要用list()函数获得所有结果并返回list。

生成素数:

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) #构造新序列

############################################

#打印100以内的素数:

for n in primes():

    if n<1000:

        print(n)

    else:

break


9.sorted

sorted()函数也是一个高阶函数,它还可以接收一个key函数来实现自定义的排序,例如按绝对值大小排序:

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

[5, 9, -12, -21, 36]

############################################

##### 给sorted传入key函数,即可实现忽略大小写的排序:

>>> sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.lower, reverse=True)

['Zoo', 'Credit', 'bob', 'about']

sorted()也是一个高阶函数。用sorted()排序的关键在于实现一个映射函数。高阶函数的抽象能力是非常强大的,而且,核心代码可以保持得非常简洁。


10.返回函数

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

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时,相关参数和变量都保存在返回的函数中,这种称为“闭包(Closure)”的程序结构拥有极大的威力。再注意一点,当我们调用lazy_sum()时,每次调用都会返回一个新的函数,即使传入相同的参数。


11.匿名函数

当我们在传入函数时,有些时候,不需要显式地定义函数,直接传入匿名函数更方便。

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

[1, 4, 9, 16, 25, 36, 49, 64, 81]

关键字 lambda 表示匿名函数, 冒号前面的 x 表示函数参数。

匿名函数有个限制,就是只能有一个表达式,不用写return,返回值就是该表达式的结果。用匿名函数有个好处,因为函数没有名字,不必担心函数名冲突。此外,匿名函数也是一个函数对象,也可以把匿名函数赋值给一个变量,再利用变量来调用该函数。Python对匿名函数的支持有限,只有一些简单的情况下可以使用匿名函数。


12.装饰器

为什么有装饰器??

1)由于函数也是一个对象,而且函数对象可以被赋值给变量,所以,通过变量也能调用该函数。    ----能懂

eg:abs是python的内置函数, f=abs   则,f(-11)  等同于 abs(-11)

2)函数对象有一个__name__属性,可以拿到函数的名字。   ----能懂

eg:abs函数,通过调用这个函数的name属性,可以得到其函数名,abs.__name__        f也可以  f.__name__

3)假设要增强 abs() 函数的功能,比如,在函数调用前后自动打印日志,但又不希望修改 abs() 函数的定义,这种在代码运行期间动态增加功能的方式,称之为“装饰器”(Decorator)。       ----不是很懂

本质上,decorator就是一个返回函数的高阶函数。所以,我们要定义一个能打印日志的decorator,可以定义如下:

####定义一个打印日志的迭代器####

def log(func):    ###接收一个函数作为输入参数

    def wrapper(*args, **kw):###定义函数

        print('call %s():' %func.__name__)

        return func(*args, **kw)  ###返回输入的函数

    return wrapper   ####decorator装饰器的本质就是返回函数的高阶函数,所以此装饰器要返回函数  wrapper

我们要借助Python的@语法,把decorator置于函数的定义处:

@log  ##借助@装饰器名  将其置于函数定义处  相当于abs=log(abs)

def abs():

### 由于log()是一个decorator,返回一个函数,所以,原来的abs()函数仍然存在,只是现在同名的abs变量指向了新的函数,于是调用abs()将执行新函数,即在log()函数中返回的wrapper()函数。

然后再调用 abs() 函数,不仅会运行 abs() 函数本身,还会在运行abs() 函数前打印一行日志。

一个完整的decorator的写法:

import functools

def log(func):

    @functools.wraps(func)#重要,写装饰器的时候加着,防止原函数的__name__属性值被修改

    def wrapper(*args, **kw):

        print('call %s():' %func.__name__)

        return func(*args, **kw)

    return wrapper

带有参数的decorator的写法:

import functools

def log(text):

    def decorator(func):

        @functools.wraps(func)

        def wrapper(*args, **kw):

            print('%s %s():' %(text, func.__name__))

            return func(*agrs, **kw)

        return wrapper

    return decorator

在面向对象(OOP)的设计模式中,decorator被称为装饰模式。OOP的装饰模式需要通过继承和组合来实现,而Python除了能支持OOP的decorator外,直接从语法层次支持decorator。Python的decorator可以用函数实现,也可以用类实现。

decorator可以增强函数的功能,定义起来虽然有点复杂,但使用起来非常灵活和方便。


13.偏函数

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

int2 = functools.partial(int, base=2)  #固定参数   转换成二进制

max2 = functools.partial(max, 10)  # 实际上会把10作为*args的一部分自动加到左边

##max2(5,6,7)  等价于  args=(10,5,6,7)   max(*args)

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 196,099评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,473评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 143,229评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,570评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,427评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,335评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,737评论 3 386
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,392评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,693评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,730评论 2 312
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,512评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,349评论 3 314
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,750评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,017评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,290评论 1 251
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,706评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,904评论 2 335

推荐阅读更多精彩内容

  • 〇、前言 本文共108张图,流量党请慎重! 历时1个半月,我把自己学习Python基础知识的框架详细梳理了一遍。 ...
    Raxxie阅读 18,897评论 17 410
  • 基础1.r''表示''内部的字符串默认不转义2.'''...'''表示多行内容3. 布尔值:True、False(...
    neo已经被使用阅读 1,651评论 0 5
  • 从来不认为鲜花玫瑰钻戒等物质点缀的告白才是最浪漫和刻骨铭心的,相反,那可能都是套路,因为最浪漫的告白是要能感受到对...
    丸丸的八卦日常阅读 362评论 0 0
  • 平平淡淡的一天下棋ing
    dq920813阅读 99评论 0 0
  • 十四、你说的是真的吗?(下) 我要用什么样的速度生活 才能与你再次相遇,为什么我已经这样努力了,可是最后的结局却依...
    墨燔阅读 274评论 0 2