Python学习笔记|高级特性

Bruce
2023-11-30 / 0 评论 / 50 阅读 / 正在检测是否收录...
温馨提示:
本文最后更新于2023年11月30日,已超过376天没有更新,若内容或图片失效,请留言反馈。

一、切片

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

# 取前三个元素
# 如果第一个索引是0,还可以省略:
L[0:3]
L[:3]
['Michael', 'Sarah', 'Tracy']

# 从索引1开始,取出2个元素出来
L[1:3]
['Sarah', 'Tracy']

# Python支持倒数切片
L[-2:]
['Bob', 'Jack']

L[-2:-1]
['Bob']

S = list(range(100))
print(S)

# 取前10个数
print(S[0:10])
# [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

# 取后10个数
print(S[-10:])
# [90, 91, 92, 93, 94, 95, 96, 97, 98, 99]

# 前11-12个数
print(S[11:22])
# [11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21]

# 前10个,每两个取一个
print(S[0:10:2])
print(S[:10:2])
# [0, 2, 4, 6, 8]

# 所有数,每5个取一个
print(S[:100:5])
# [0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95]

# 复制一个list
print(S[:])

T = (1,2,3,4,5,6,7,8,9,10)
print(T[:3])
# (1, 2, 3)

# 字符串也可以看做成一个list
Str = 'ABCDEFG'
print(Str[0:3])
# ABC
# 利用切片操作,实现一个trim()函数,去除字符串首尾的空格
def trim(s):
    start = 0
    end = len(s)
    
    # 找到字符串首部的空格
    while start < end and s[start] == ' ':
        start += 1
    
    # 找到字符串尾部的空格
    while end > start and s[end-1] == ' ':
        end -= 1
    
    return s[start:end]

二、迭代

如果给定一个listtuple,我们可以通过for循环来遍历这个listtuple,这种遍历我们称为迭代(Iteration)。

字典可以迭代:

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

字符串可以迭代

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

# 同时迭代key和value
for key, value in d.items()
    print(key,value)
    
# 输出结果
a
c
b
1
2
3
a 1
b 2
c 3

'''
items()是一个字典(dict)的方法,用于返回字典中所有键值对的元组。每个元组包含两个元素,第一个元素是键(key),第二个元素是对应的值(value)。

在for key, value in d.items()这个语句中,d是一个字典对象,items()方法会返回一个包含所有键值对的可迭代对象。通过for循环遍历这个可迭代对象,每次迭代时,将键值对解包为key和value两个变量,可以对每个键值对进行操作。
'''

使用迭代查找一个list中最小和最大值,并返回一个tuple:

def findMinAndMax(L):
    #没有初始化max_value
    for i in L:
        if i > L[0]:
            max_value = i
        if i < L[0]:
            min_value = i
    print(max_value,min_value)
findMinAndMax([1,2,3,1])

# 报错
'''
Traceback (most recent call last):
  File "D:\pythonProject\高级特性\迭代.py", line 44, in <module>
    findMinAndMax([1,2,3,1])
  File "D:\pythonProject\高级特性\迭代.py", line 43, in findMinAndMax
    print(max_value,min_value)
UnboundLocalError: local variable 'min_value' referenced before assignment
'''

# 解释报错原因
'''
在您提供的代码中,max_value和min_value在某些条件下才会被赋值。如果这些条件从未满足,这些变量将不会被初始化,导致UnboundLocalError。
在您的代码中,由于列表[1,2,3]中的所有元素都不会小于第一个元素(L[0]),min_value从未被赋值。
此外,即使这些条件满足,这些变量也只在if语句的作用域内部有效,而不是整个函数的作用域。
为了确保这些变量在整个函数中都是可用的,您需要在循环之前初始化它们。
此外,您的比较逻辑不应该只与列表的第一个元素比较,而应该与当前已知的最大值或最小值比较。
'''

# 修复代码
def findMinAndMax(L):
    if not L:  # 如果列表为空,返回None
        return (None, None)

    max_value = L[0]  # 初始化为列表的第一个元素
    min_value = L[0]  # 初始化为列表的第一个元素

    for i in L:
        if i > max_value:  # 如果当前元素大于已知最大值
            max_value = i
        if i < min_value:  # 如果当前元素小于已知最小值
            min_value = i

    print(max_value, min_value)  # 在这里打印最大值和最小值


findMinAndMax([1, 2, 3])

三、列表生成式

# 生成[1*1,2*2,3*3,4*4,...,10*10]
L =[]
for i in range(1,11):
    L.append(i * i)
print(L)

# 使用列表生成器
A = [x*x for x in range(1,11)]
print(A)

# 使用if判断
B = [x*x for x in range(1,11) if x%2 ==0]
print(B)

# 使用两层循环生成全排列
C = [m+n for m in 'ABC' for n in 'XYZ']
print(C)

# 使用运用列表生成式列出当前文件夹下的所有文件名称
import os
D= [d for d in os.listdir('.')] # os.listdir可以列出文件和目录
print(D)

# for循环可以同时使用多个变量,如dict的items(),可以同时迭代key和value
dict = {'A':'X','B':'Y','C':'Z'}
for key,value in dict.items():
    print(key, '=', value)


# 使用两个变量来生成list
dict = {'A':'X','B':'Y','C':'Z'}
F = [k+'='+v for k,v in dict.items()]
print(F)

# 将大写变成小写
L = ['Hello', 'World', 'IBM', 'Apple']
G = [l.lower() for l in L]
print(G)

# if...else..用法
# 示例
'''
H = [x for x in range(1,11) if x%2 == 0]
print(H)
'''
# 输出结果
'''
  File "D:\pythonProject\高级特性\列表生成式.py", line 45
    I = [x if x%2 == 0 for x in range(1,11)]
         ^^^^^^^^^^^^^
SyntaxError: expected 'else' after 'if' expression
'''

# if写在for前面的时候,前面必须加else,否则报错
# 这是因为,for前面是一个表达式,它必须根据x计算出一个结果。表达式if x%2 == 0无法根据x计算出结果,因为缺少else

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

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

四、生成器

通过列表生成式,可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。如果列表元素可以按照某种算法推算出来,那么就可以在循环的过程中不断推算出后续的元素。

在Python中,这种一边循环一边计算的机制,称为生成器: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>

创建Lg的区别仅在于最外层的[]()L是一个list,而g是一个generator。

通过next()函数获得generator的下一个返回值:

>>> next(g)
0
>>> next(g)
1
>>> next(g)
4
>>> next(g)
9
>>> next(g)
16
>>> next(g)
25
>>> next(g)
36
>>> next(g)
49
>>> next(g)
64
>>> next(g)
81
>>> next(g)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

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

通过for循环来迭代generator,可以不需要关心StopIteration的错误。

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

方法二、通过函数实现

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

比如,著名的斐波拉契数列(Fibonacci),除第一个和第二个数外,任意一个数都可由前两个数相加得到:

1, 1, 2, 3, 5, 8, 13, 21, 34, ...

可以用函数表示为:

def fib(max):
    n, a, b = 0, 0, 1
    while n < max:
        print(b)
        a, b = b, a + b
        n = n + 1
    return 'done'

要把fib函数变成generator函数,只需要把print(b)改为yield b即可:

def fib(max):
    n, a, b = 0, 0, 1
    while n < max:
        yield b
        a, b = b, a + b
        n = n + 1
    return 'done'

调用函数:

>>> f = fib(6)
>>> f
<generator object fib at 0x104feaaa0>

相较于普通函数,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 "<stdin>", line 1, in <module>
StopIteration

next()函数的一种错误用法:

>>> next(odd())
step 1
1
>>> next(odd())
step 1
1
>>> next(odd())
step 1
1

原因在于odd()会创建一个新的generator对象,上述代码实际上创建了3个完全独立的generator,对3个generator分别调用next()当然每个都会返回第一个值。

正确的写法是创建一个generator对象,然后不断对这一个generator对象调用next()

>>> g = odd()
>>> next(g)
step 1
1
>>> next(g)
step 2
3
>>> next(g)
step 3
5

五、迭代器

可以使用for循环的数据类型有以下几种:

一类是集合数据类型,如listtupledictsetstr等;

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

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

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

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

(二)使用isinstance()判断一个对象是否是Iterator对象:

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

生成器都是Iterator对象,但listdictstr虽然是Iterable,却不是Iterator

listdictstrIterable变成Iterator可以使用iter()函数:

>>> isinstance(iter([]), Iterator)
True
>>> isinstance(iter('abc'), Iterator)
True

(三)总结

  1. 凡是可作用于for循环的对象都是Iterable类型;
  2. 凡是可作用于next()函数的对象都是Iterator类型,它们表示一个惰性计算的序列;
  3. 集合数据类型如listdictstr等是Iterable但不是Iterator,不过可以通过iter()函数获得一个Iterator对象。
1

评论 (0)

取消