iter()函数联队*、zip()实现序列“定长”拆分——基于iterator特性拆解繁复的单行“装逼代码”,搞明白序列定长拆分“秘法”

156 篇文章 3 订阅

Python 官网https://www.python.org/




  自学并不是什么神秘的东西,一个人一辈子自学的时间总是比在学校学习的时间长,没有老师的时候总是比有老师的时候多。
            —— 华罗庚


等风来,不如追风去……


Python iter()用法简述
iter()奇妙用法
(“定长”拆分序列)


目 录


单行装逼代码

print(''.join(chr(int(''.join(i), 16)) for i in zip(*[iter('576f772c2049276d2077726974696e6720696e2068657861646563696d616c21')]*2)))

  Run这行代码,会输出下面那句话。我感觉好神奇!决定Study……


Wow, I'm writing in hexadecimal!

  仔细“研究”、拆分后,发现是zip()对同一个迭代器作用,产生的Magic(魔法)。这又让我想要对iter()“钻研”了一番。😏


回目录

  先看看iter()的“文档”

print(iter.__doc__)

iter(iterable) -> iterator
iter(callable, sentinel) -> iterator
Get an iterator from an object. In the first form, the argument must supply its own iterator, or be a sequence.
In the second form, the callable is called until it returns the sentinel.

  “文档”显示:iter()有给定一个参数、给定两个参数,两种用法。

一、iter(iterable) -> iterator
  当只给定一个参数时,参数必须是可迭代对象(iterable object),返回那个参数(iterable object)的iterator(迭代器)。换句话说,就是iter()赋予了iterable object能一个一个返回内容物(元素)的能力,就是让iterable object具有了__next__()“魔法”方法。可以用内置函数next()实现,也可以用自身“新”得技能(next()方法)实现。iterable object参数可以是符合条件的“任意”Python对象。如:

/sdcard/qpython $ python
Python 3.10.2 (main, Mar  1 2022, 12:58:06) [Clang 12.0.8 (https://android.googlesource.com/toolchain/llvm-project c935d99d7 on linux
Type "help", "copyright", "credits" or "license" for more information.                              
>>> iter('ok')
<str_iterator object at 0x79d5ca4460>
>>> iter([3, 4])
<list_iterator object at 0x79d5c21c90>          
>>> iter((6, 'c'))
<tuple_iterator object at 0x79d5c5aa70>
>>> iter({3,9})
<set_iterator object at 0x79d5ca3a80>
>>> iter({5: 6, 'ok': 'haha'})                  
<dict_keyiterator object at 0x79d5c36700>
>>>>

  如您所见,各种对象返回的迭代器,都留着各自的“烙印”。字符串、列表、元组、集合、字典key,这Python五大基础类型,都带着各自的印记。这对“她”作为迭代器使用,毫无阻滞。下面就来炼炼:

>>> iter('ok').__next__()
'o'
>>> iter([3, 4]).__next__()
3
>>> iter((6, 'c')).__next__()
6
>>> iter(('c', 9)).__next__()
'c'
>>> iter({3, 9}).__next__()
9
>>> d = iter({5: 6, 'ok': 'haha'})
>>> d.__next__()
5
>>> d.__next__()
'ok'
>>>

  一个一个迭代器,可以变着花样使。

  Python3.x内置函数zip()、map()返回的就是iterator。如:

>>> z = zip((4, 6), list('Good'))
>>> z
<zip object at 0x78cffa1380>
>>> z.__next__()
(4, 'G')
>>> z.__next__()
(6, 'o')
>>> z.__next__()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
>>> m = map(str, range(6, 9))
>>> m
<map object at 0x78cff99de0>
>>> next(m, 'The end.')
'6'
>>> next(m, 'The end.')
'7'
>>> next(m, 'The end.')
'8'
>>> next(m, 'The end.')
'The end.'
>>>

  由前面“操作”可以看出,zip object、map object她就是iterator,她们有__next__()方法,内置函数next()也可以调用。

>>> next(zip((4,), ['d']))
(4, 'd')
>>> map(int, list('456')).__next__()
4

回目录

iter(object)配合zip()的“特殊”用法

  iter()借助zip()和*星号“联众”之力,可以实现用简洁的代码完成序列定长选取。例:

  为简化例子,先定义了个函数mychoice(iterator, num)。

>>> def mychoice(iterator, num):
...     ''' iterator -> 迭代器对象
...         num -> int,连续取值数量 '''
...     return zip(*[iterator]*num)
...
>>> print(*mychoice(iter(range(9)), 2))
(0, 1) (2, 3) (4, 5) (6, 7)
>>> print(*mychoice(iter('Chongqing'), 3))
('C', 'h', 'o') ('n', 'g', 'q') ('i', 'n', 'g')
>>>

  神不神奇,惊不惊喜。😜这,就是本笔记开篇提到的“装逼代码”的“奇妙”用法。您也许已经发现,由于zip()的特性,不能完全匹配成组的序列元素,被舍弃。比如还原十六进制编码的字符,四位或者两位都是没有余数的,所以对“实际”的应用,几乎没有影响。这,是如何实现的哩?我想是基于迭代器“只可以读取一次”的特性,在iter()官方文档没有找到相关说明。因为我在这里zip()的参数,都是同一个迭代器,重复几次迭代器就是实现连续取几个序列元素。

上面的return 语句,也可以写成

return zip(iterator, ierator, ...) # 想连续取几个序列元素就重复几次。

举个粟子

>>>
>>> iterator = iter('I love you.')
>>> tem = iterator
>>> print(*zip(tem, tem, tem))
('I', ' ', 'l') ('o', 'v', 'e') (' ', 'y', 'o')
>>>

  对同一个迭代器,在zip()参数中重复三次,就完成了连续三个字符的选取。

  利用这一“特性”,可以“异想天开”的做些事情。例如:把十六进制字符编码还原成“本来的样子”。

s = '我是重庆“梦幻精灵_cq”,我爱Python,is studying 一条十六进制码搞怪语句。'
en = "Wow, I'm writing in hexadecimal!"
hex_s = '576F772C2049276D2077726974696E6720696E2068657861646563696D616C21'

def to_hex(s, num=2):
    ''' 将字符转换成十六进制编码,\n字符含中文num=4,\n默认纯英文。 '''
    return ''.join(map(lambda x: hex(ord(x))[2:].upper().zfill(num), s)) # 转换字符编码并去除十六进制标识字符用设置位数存储,不足位补“0”。无缝拼接字符,返回字符串。

if __name__== '__main__':
    print(f"\n原字符串:{s}\n十六进制:\n{to_hex(s, 4)}") # 用四位十六进制字符保存字符编码。
    print(f"\n原字符串:{en}\n十六进制:\n{to_hex(en)}") # 用to_hex()默认参数转换英文字符成2位十六进制ASCII编码。

b = zip(*[iter(to_hex(s, 4))]*4) # 转换字符成十六进制字符编码。
print(f"\n十六进制编码字符串:\n{[''.join(i) for i in b]}\n") # 打印十六进制字符编码字符串。
b = zip(*[iter(to_hex(s, 4))]*4) # 转换字符成十六进制字符编码。

for i in b: # 遍历还原字符。
    print(chr(int(''.join(i), 16)), end="")

输出截屏图片
在这里插入图片描述
  在对迭代器变量b的操作中,为什么会对其两次赋值。因为b在打印十六进制字符编码列表时,已读取b所以再对b操作得先赋值。(iterator只可以读取一次。)

  也可见只用一条复合语句完成还原,或者转换 -> 还原整个过程。不过,“较长”的复合语句“生涩难读”,除了“装逼”,少用为宜。

print(f"\n\n{''.join([chr(int(''.join(i), 16)) for i in zip(*[iter(hex_s)]*2)])}")
print(f"\n{''.join(map(lambda x: chr(int(''.join(x),16)), zip(*[iter(to_hex(s,4))]*4)))}\n")

输出截屏图片
在这里插入图片描述


回目录

二、iter(iterable, sentinel) -> iterator
  如果给出第二参数sentinel,第一个参数必须是可调用对象(不带参数的函数或者类方法,且每次调用会返回一个值),循环调用第一个参数,直到==第二参数(sentinel的值)正如sentinel的释义一样,就是——“哨兵”——到此为止。不返回sentinel的值。如:

or more information.
>>> lis = range(9)
>>> iterator = iter(lis)
>>> tem = iter(iterator.__next__, 7)
>>> tem
<callable_iterator object at 0x78d0163940>
>>> print(*tem)
0 1 2 3 4 5 6
>>>
>>> iterator = iter('I am Dream elf, I live in Chongqing.')
>>> print(iter(iterator.__next__, ','))
<callable_iterator object at 0x78d01638b0>
>>> print(*iter(iterator.__next__, ','))
I   a m   D r e a m   e l f

  当第二参数sentinel是7时,就输出她前面的0~6,不输出7;当sentinel是“,”,就输出前半句字符串,不带输出“,”。


回目录

iter()官方文档:
文档链接https://docs.python.org/3/library/functions.html#iter

文档内容,
iter(object[, sentinel])
Return an iterator object. The first argument(参数) is interpreted very differently depending on the presence of the second argument. Without a second argument, object must be a collection object which supports the iterable protocol (the iter() method), or it must support the sequence protocol (the getitem() method with integer arguments starting at 0). If it does not support either of those protocols, TypeError is raised. If the second argument, sentinel, is given, then object must be a callable object. The iterator created in this case will call object with no arguments for each call to its next() method; if the value returned is equal to sentinel, StopIteration will be raised, otherwise the value will be returned.

See also Iterator Types.

One useful application of the second form of iter() is to build a block-reader. For example, reading fixed-width blocks from a binary database file until the end of file is reached:

from functools import partial
with open(‘mydata.db’, ‘rb’) as f:
for block in iter(partial(f.read, 64), b’’):
process_block(block)


参考CSDN博文:



回首页

__上一篇:__ 根据给定字符数和字符,打印输出“沙漏”和剩余数

__下一篇:__ 单词记忆系统二:音标字符输入

我的HOT博:
推荐条件 点阅破千

回目录


老齐漫画头像

精品文章:

来源:老齐教室


回目录

Python 入门指南【Python 3.6.3】

好文力荐:

CSDN实用技巧博文:


  • 10
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 12
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

梦幻精灵_cq

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值