06 python中的高级特性 - lovemoganna/DataStructure GitHub Wiki

代码简洁性

# 输出1,3,5,7,...99

L=[]
n= 1
while n<= 100:
    L.append(n)
    n = n +2
print(L)    
# 代码越少,开发效率越高.    

# 下面这行代码就可以代替啦:

[x for x in range(1,100)]
  File "<ipython-input-219-f1ea90287ed7>", line 11
    [x for x in range(1,100)\n]
                               ^
SyntaxError: unexpected character after line continuation character

list/Tuple的切片

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

# 取前面3个元素
[L[0],L[1],L[2]]

# 切面的表达
L[0:3]
L[:3] # 索引如果是0 开始,可以省略掉.
L[-1:] # 倒着只能取1个元素

# 千万记住倒数第一个元素的索引是-1,正着数第一个元素的索引是0.
['Jack']
# 循环的方式表达,取出3个元素.
r =[]
n =3 
for i in range(n):
    r.append(L[i])

print(r)
['Michael', 'Sarah', 'Tracy']
# 创建一个0-99的数列
L = list(range(100))
L[:10] # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
L[-10:] # [90, 91, 92, 93, 94, 95, 96, 97, 98, 99]
L[10:20]
L[:10:3] # [0, 3, 6, 9] 前10个数,每3个取一个.
L[::5] # 所有数,每5个取一个
L[:] # 复制一个list

(0,1,2,3,4,5)[:3] # 取Tuple中的前3个数.还是一个Tuple
'ABCDEFG'[:2] # 取出前2个字符

# 截取字符串
'ABCDEFG'[::2] # 取出间隔为2的所有字符,从A开始算,也就是'ACEG'
'ABCDEFG'[1:] # 除去第一个字符串的所有部分
'ABCDEFG'[-1:]# 除去最后一个字符串的所有部分
'BCDEFG'
def trim(s):
    # 字符串的前面是空格
    while s[:1] == ' ':
        s = s[1:]
    # 字符串的后面是空格
    while s[-1:] == ' ':
        s = s[:-1]
    return s
trim(' hello  ')
'hello'

迭代

# c 语言中的list的遍历
for(i =0;i<list.length;i++){
    n=list[i];
}
# python 主要是通过 for ... in 来进行迭代的,比较抽象

# 下面迭代一个dict
ds={'a':'1','c':'2','d':'3'}

for d in ds:
    # 打印dict中的key和value
    print('key=',d,'value=',ds.get(d))

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


# 下面迭代出所有的value
for value in ds.values():
    print(value)


# 下面迭代出所有的key和value
for k, v in ds.items():
    print(k+":"+v)
    
# 字符串也能迭代    
for ch in 'ABC':
    print(ch)

    
key= a value= 1
key= c value= 2
key= d value= 3
1
2
3
a:1
c:2
d:3
A
B
C
# python是如何判断对象是否可进行迭代的哪?

from collections import Iterable
isinstance('abc',Iterable) # True 代表字符串可以迭代.

isinstance((1,2,3),Iterable) # list也可以迭代
isinstance({'a':'1','b':'2','c':'3'},Iterable) # dict也可以迭代
True
# 对list实现类似Java那样的下标循环
# Python内置的enumerate函数可以把一个list变成索引-元素对,这样就可以在for循环中同时迭代索引和元素本身:

for i , value in enumerate(['a','b','c']):
    print(i,value)

print()
# for循环里,同时引用了两个变量的情况
for x,y in [(1,2),(2,3),(3,4)]:
    print(x,y)
                            
0 a
1 b
2 c

1 2
2 3
3 4
# 发现最大值和最小值
def findMinAndMax(L):
    # 判断list为0的情况
    if len(L) == 0:
        return(None, None)
    else:
        max = L[0]
        min = L[0]
    # list不为0 的情况
    for s in L:
        if s > max:
            max = s
        if s < min:
            min = s
    return(min, max)
findMinAndMax([0,1,2,3,4,5,6,6])
(0, 0)
def findMinAndMax(L):
    if len(L) == 0:
        return (None, None)
    else:
        s_max = max(L)
        s_min = min(L)
        return (s_min, s_max)
findMinAndMax([0,1,2,3,4,5,6,6])    
(0, 6)
def findMaxAndMin(L):
    if len(L)==0:
        return None
    else:
        s_max=max(L)
        s_min=min(L)
        return (s_min,s_max)
findMaxAndMin([0])    
(0, 0)

列表生成式

# list(range(1, 11))

# 1-10的自乘
L=[]
for x in range(1,11):
    L.append(x*x)
L    

[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]# 加上条件判断的写法
[m + n for m in 'ABC' for n in'XYZ'] # 双层循环:['AX', 'AY', 'AZ', 'BX', 'BY', 'BZ', 'CX', 'CY', 'CZ']
['AX', 'AY', 'AZ', 'BX', 'BY', 'BZ', 'CX', 'CY', 'CZ']
import os # 导入os模块.
[d for d in os.listdir('.')] #  # os.listdir可以列出文件和目录
['.ipynb_checkpoints',
 '04-settest.ipynb',
 '05-函数.ipynb',
 'beautifulsoup.ipynb',
 'cookie.txt',
 'notebook.tex',
 'python06-高级特性.ipynb',
 'regex.ipynb',
 'requests.ipynb',
 'Untitled.ipynb',
 'urllib.ipynb',
 '基本语法.ipynb',
 '集合和数组.ipynb']
d={'x':'1','y':'2'}
for k,v in d.items():
    print(k,v)

# 列表生成式的表述
[k + '=' + v for k, v in d.items()]

L=['Hello','World','IBM','Apple']
[s.lower() for s in L] # 将所有字符串小写

# 只有字符串才能使用lower()方法
L = ['Hello', 'World', 18, 'Apple', None]
# [s.lower() for s in L] # 'int' object has no attribute 'lower'

# 使用内建的isinstance函数可以判断一个变量是不是字符串
x = 'abc'
y=123
isinstance(x,str) # True
isinstance(y,str) # False
x 1
y 2





False

生成器

# 在python里面,一边循环一边计算的机制,称为生成器:generator

L = [x*x for x in range(10)]
L # L是一个list
g=(x * x for x in range(10))
g # g 是一个生成器

# 如果要一个一个打印出来,可以通过next()函数来获得generator的下一个返回值
next(g) # 0
next(g) # 1
# ...依次类推

# generator保存的是算法,每次调用next(g),就计算出g的下一个元素的值,直到计算到最后一个元素,
# 没有更多的元素时,抛出StopIteration的错误。


#更加方便的调用方法是for循环,因为generator也是一个可迭代对象

g = (x * x for x in range(10))
for i in g:
    print(i)
    
0
1
4
9
16
25
36
49
64
81

著名的斐波拉契数列(Fibonacci),除第一个和第二个数外,任意一个数都可由前两个数相加得到: 1, 1, 2, 3, 5, 8, 13, 21, 34, ...

# 菲波那切数列
def fib(max):
    n, a, b = 0, 0, 1
    while n < max:
        # print(b)
        yield b
        a, b = b, a + b
        n = n + 1
    return 'done'
f=fib(6)
f 

# 此时我们可以通过循环来取得generator里面的值,而不必通过next()
for i in f:
    print(i)
    

    
# 但是此时我们发现拿不到return 里面的值.其实就是一个捕获异常的表达
g = fib(6)
# 加上这个就相当于不断的去判断是否符合条件.
while True:
    try:
        x= next(g)
        print('g',x)
    except StopIteration as e:
        print('Generator return value:',e.value)
        break
        
#<generator object fib at 0x0000000004DFB990>
# 此时想要把fib函数变为generator.只需要将b,改为 yield b就可以了
# 如果一个函数定义中包含yield关键字,那么这个函数就不再是一个普通函数,而是一个generator:
1
1
2
3
5
8
g 1
g 1
g 2
g 3
g 5
g 8
Generator return value: done
t = (b,a+b) # t 是一个Tuple
a = t[0]
b = t[1]
# 就相当于 t = (t(1),t(0) + t(1))
a,b=(1,2)
print(a)
print(b)
# fib函数可以从第一个元素开始,推算出后续任意的元素,这种逻辑其实非常类似generator。

generator和函数执行的流程:

1.函数是顺序执行,遇到return语句或者最后一行函数语句就返回,

2.变成generator的函数,在每次调用next()函数的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句执行.

def odd():
    print('step1')
    yield 1
    print('step2')
    yield (3)
    print('step3')
    yield(5)

o = odd()# 调用的时候,先要生成一个generator的对象,然后用next()不断获得下一个返回值.
next(o) # step1 1
next(o) # step2 3
next(o) # step3 5
next(o) # StopIteration: 


# 因为加了yield关键字,所以odd函数已经变为了generator,在执行的过程中,遇到yield就会中断,下次可以再次在中断的位置执行.

# 执行了3次next()后,没有yield可以执行了.
step1
step2
step3



---------------------------------------------------------------------------

StopIteration                             Traceback (most recent call last)

<ipython-input-179-03039e004379> in <module>()
     11 next(o)
     12 next(o)
---> 13 next(o)


StopIteration: 

迭代器

直接做for循环的数据类型:

1.集合数据类型:list,tuple,dict,set,str

2.generator类型:包括生成器和带yield的generator function

这些直接作用for循环的对象统称为可迭代对象,统一称为Iterable.

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

from collections import Iterable,Iterator
isinstance([],Iterable)
isinstance({},Iterable)
isinstance('abc',Iterable)
isinstance((x for x in range(10)),Iterable)
#isinstance(100,Iterable) # False,可变对象.
isinstance(iter([]), Iterator)
isinstance(iter({}), Iterator)
isinstance(iter('abc'), Iterator)
True

此时需要记住:

1.生成器都是Iterator对象. list,dict,str是Iterable,但不是Iterator.

2.将list,dict,str等Iterable变成Iterator可以使用iter()函数.

python中的Iterator对象表示的是一个数据流,Iterator对象可以被next()函数调用并不断返回下一个数据.直到没有抛出StopIteration错误.

可以把这个数据流看做是一个有序序列.但我们不能提前知道序列的长度,只能不断的通过next()函数实现按需计算下一个数据.

所以Iterator的计算是惰性的,只有在需要返回下一个数据的时候他才会计算.

Iterator甚至可以表示一个无限大的数据流,例如全体自然数。而使用list是永远不可能存储全体自然数的。

总结

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

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

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

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

[x for x in range(1,11)]
L=(x for x in range(1,11))# 这就相当于一个generator,生成器.
next(L)
next(L)


# for循环的表示

for x in [1,2,3,4,5]:
    print(x)

# 等价于下面的next()函数的不断调用
# 先获得Iterator对象
it=iter([1,2,3,4,5])
# 循环
while True:
    try:
        #获得下一个值
        next(it)
    except StopIteration:
        #遇到StopIteration就退出循环
        break
        
# 复习一下带yield参数的生成器

def oh(a):
    if a >= 18:
        yield('你成年了,可以随便狼了!')
    else:
        yield('你未成年,没有足够的自由!')
next(oh(80))
next(oh(12))
1
2
3
4
5





'你未成年,没有足够的自由!'
⚠️ **GitHub.com Fallback** ⚠️