当前位置: 移动技术网 > IT编程>脚本编程>Python > 荐 Python进阶语法笔记(1)

荐 Python进阶语法笔记(1)

2020年07月12日  | 移动技术网IT编程  | 我要评论

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()函数时

  1. 最好使用for循环迭代,因为open()函数代开文件是一个可迭代对象,对象内部包含并含有处理 __next__() 异常的方法。
  2. 可以使用保留方法 __next__ () 一行一行的读,使用一次之后指针就指向下一行的开始位置,到最尾部超出之后就会抛异常。
  3. 也可以使用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__() 低。

  1. 迭代之前首先调用内置函数 iter( X ) ,返回操作对象X的 __iter__() 方法形成的迭代对象Y,若X没有 __iter__() 就转而调用 __getitem__() 方法进行索引迭代
  2. 获取迭代对象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

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网