荐 Python进阶语法笔记(1)
1. 读文件与内存联系
fp = open('a.txt', 'r').read() # 读文件所有内容,若内容较大会占用内存
fp = open('a.txt', 'r').readline() # 只读取文件一行内容,不太占用内存
fp = open('a.txt', 'r').readlines() # 读文件每一行放到列表,若内容较大会占用内存
由于每读取一行,尾部就会有换行符 \n ,可以使用 string.rstrip( [指定清除字符] ) 方法去掉换行符。
rstrip()函数作用:首先指定字符参数,若不指定则默认是空格,然后在字符串末尾寻找该字符参数,然后删掉该字符参数,最后返回结果。
该函数使用的算法比较特殊。比如string = ‘abcdbaab’,string.rstrip(‘ab’)首先从字符串尾部开始寻找,发现之后删除剩余abcdba,然后继续向前寻找,由于算法原因,将反过来的参数ba也当成目标,删除之后剩余abcd,然后继续向前发现了d,不是目标就停止向前,最后返回结果。
使用open()函数时
- 最好使用for循环迭代,因为open()函数代开文件是一个可迭代对象,对象内部包含并含有处理 __next__() 异常的方法。
- 可以使用保留方法 __next__ () 一行一行的读,使用一次之后指针就指向下一行的开始位置,到最尾部超出之后就会抛异常。
- 也可以使用next()内置函数,next()函数底部原理就是使用了__next__() 函数,同样在最尾部超出时抛异常。
2. for实现解析操作
for实现的解析操作,在Python解释器中是使用C语言来实现运行的,运行速度较快。
# 第一种写法
L1 = []
for i in range(10):
if i % 2 == 0:
L1.append(i**2)
# 第二种写法
L2 = [i**2 for i in range(10) if i % 2 == 0]
3. for与while
- for是通过in的元素存在性测试来循环的,即迭代遍历
- while是通过条件判断的真假来循环的,即普通的步进循环
for比while运行更快,因为for在解释器中是使用C语言写的,while是Python虚拟机的解释代码。
4.迭代器
dir( list ) 时发现 (list | tuple | dict | set) 容器类型里面有 __iter__() 方法,该函数用来返回一个迭代对象,迭代对象 **必须** 要同时有__iter__() 方法和 __next__() 方法才能称为迭代器(或称可迭代对象)。所以:从迭代对象中获取迭代器(可迭代对象)。
对于for/in/map/zip等迭代工具运行过程中,不一定要实现 __iter__() ,实现 __getitem__() 方法也是可以的,由于 __getitem__() 是数值索引迭代的方式,所以优先级比 __iter__() 低。
- 迭代之前首先调用内置函数 iter( X ) ,返回操作对象X的 __iter__() 方法形成的迭代对象Y,若X没有 __iter__() 就转而调用 __getitem__() 方法进行索引迭代
- 获取迭代对象Y后,迭代过程中每次都调用next() 内置函数生成结果,而next() 会自动调用迭代对象Y的 __next__()
5. 自定义迭代 之 索引取值和分片取值
列表、元组、字典、集合、字符串等之所以可以索引取值、分片取值,是因为它们实现了**__getitem__( )**方法
slice对象由slice()内置函数创建,它有三个参数:起始索引位、结束索引位、步进位,如
a = [11, 22, 33, 44, 55, 66, 77]
a[slice(0, 5, 2)]
[11, 33, 55]
分片操作过程中,实际上是将一个slice对象当作索引位传递给序列,然后以索引的方式获取元素,如
L = [33, 66, 88, 99]
L[0:2] # [33, 66]
L[slice(0, 2)] # [33, 66]
# 同时实现了索引取值和分片取值
class cls(object):
def __init__(self, date):
self.date = date
def __getitem__(self, index):
print('getitem index is', index)
return self.date[index]
c = cls([11, 22, 33, 44, 55, 66, 77, 88]) # 实例化对象
c[3] # 索引取值
# getitem index is 3
# 44
c[1:7:2] # 分片取值
# getitem index is slice(1, 7, 2)
# [22, 44, 66]
6. 索引赋值、分片赋值、删除索引值、删除分片值
-
如果想要索引赋值或者分片赋值,就会调用__setitem__( )方法
-
如果想要删除索引值或者删除分片值,就会调用__setitem__( )方法
当我们直接输出某个实例化对象时,就自动输出’<类名 object at 内存地址>'格式的字符串。可我们想要输出指定的信息,以增强我们对该对象内部基本信息的理解,可以在类中使用__repr__( )方法,输出我们指定的信息。
class cls:
def __init__(self,data):
self._data = data
def __getitem__(self,index):
print("in getitem")
return self._data[index]
def __setitem__(self,index,value):
print("in setitem")
self._data[index] = value
def __delitem__(self,index):
print("in delitem")
del self._data[index]
def __repr__(self):
return str(self._data)
c = cls([11,22,33,44,55]) # 实例化对象
c[1:3] # 分片取值操作
# in getitem
# [22, 33]
c[1:3] = [2222,3333] # 分片赋值操作
# in setitem
c # 调用__repr__()方法,输出指定信息
# [11, 2222, 3333, 44, 55]
del c[1:3] # 删除操作
# in delitem
7. 生成器 ( yield )
生成器是特定的迭代器,它完全实现了迭代器接口,所以所有生成器都是迭代器。迭代器用于从数据中取出元素,而生成器用于生成元素。但是它不会一次性的生成所有元素,而是按需一个一个地生成,所以它永远只需占用一个元素的内存空间。生成器是一个函数,当它的行为像一个迭代器,而且Python也支持生成器表达式。
def my_generator(chars):
for i in chars:
yield i * 2
for i in my_generator("abcdef"):
print(i, end=" ")
# aa bb cc dd ee ff
E = my_generator("abcde")
hasattr(E, "__iter__") # 判断是否含有__iter__()方法
# True
hasattr(E, "__next__") # 判断是否含有__next__()方法
# True
由于my_generator()函数使用了yield关键字,就会被声名为generator对象,即生成器。外部for中调用my_generator( )函数时,函数执行内部for循环到关键字yield后就返回一个值(此处相当于return i * 2),然后保留此次运行信息并停止运行,等待下一次的调用,外部for继续取生成器的下一个值,执行my_generator()函数时就从关键字yield上一次的后面开始执行,即继续内部for循环的内部迭代。重复以上操作,直到内部for循环迭代结束。
生成器内部有__iter__( )方法和__next__( )方法,且__iter__( )方法返回的是迭代器自身。
当yield的来源为一个for循环,就可以改写为yield from。下面两个是等价的。
def my_gener(chars):
for i in chars:
yield i
def my_gener(chars):
yield from i
一开始调用生成器函数的时候并没有运行函数体中的代码,他仅仅只是返回一个生成器对象,只有开始迭代的时候,才真正的开始执行函数体。且在yield之前的代码体只执行一次(不包含yield所在的代码体),在yield之后的代码体只在yield结束的时候才执行(不包含yield所在的代码体)。yield是一个表达式,所以他是有返回值的(类似执行成功返回1,失败返回0),但在返回一个操作数之后就会挂起该函数,所以表达式的返回值是在下一次迭代时才生成的。表达式产生的返回值并没有被返回给迭代器,而且如果生成器中没有接收表达式的返回值,该返回值就会被丢弃。yield被挂起之后可以使用next( )(内部使用了__next__( )方法)或生成器的send( )方法唤醒,即继续下一次迭代。使用next( )唤醒时yield表达式的返回值为None,使用my_gener.send(xxx)唤醒时yield表达式的返回值为整形xxx参数。
8. 生成器表达式和列表表达式
列表解析(字典解析、集合解析)时使用中括号(大括号)包围的for表达式,而生成器使用小括号包围for表达式。
生成器表达式一般用来写逻辑较为简单的生成器对象;生成器函数代码量虽多,但可以写复杂逻辑的生成器对象。
# 列表表达式,使用[]中括号
>>> y = [i*2 for i in range(5, 9)]
>>> y
[10, 12, 14, 16]
# 生成器表达式,使用()小括号
>>> x = (j*2 for j in range(5, 9))
>>> x
<generator object <genexpr> at 0x00000146EC0602A0>
9. range( )函数
range( ) 函数既不是迭代器,又不是生成器,即使它的外在行为看来有点像。由以下例子可以看出不能使用__next__()方法就已经不是迭代器了,同样也不是生成器。
for解析操作的对象是迭代器,为什么他不是迭代器也能for操作?因为在for解析前就默认调用iter()内置函数获得迭代器。
haha = range(10)
haha # range(0, 10)
next(haha) # TypeError: 'range' object is not an iterator
ma = iter(ma) # iter()获得迭代器,返回对象信息
list(ma) # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 使用list()获得迭代器
本文地址:https://blog.csdn.net/qq_43279457/article/details/107271001
上一篇: PHP实现的MD5结合RSA签名算法实例