记录我的 python 学习历程-Day11 两个被忽视的坑、补充知识点、函数名的应用、新版格式化输出、迭代器
补充知识点
函数形参中默认参数的陷阱
针对不可变数据类型,它是没有陷阱的
def func(name, sex='男'):
print(name)
print(sex) func('Dylan') # 输出结果:
Dylan
男
陷阱只针对默认参数是可变的数据类型
def func(name, a_list=[]): # a_list=[]不在全局名称空间里,也不在局部名称空间里,它在一个特殊的内存空间中
a_list.append(name) # 将实参传进来的 name 增加到 a_list 这个空列表里.
return a_list # 返回 a_list 这个列表 ret = func('Dylan')
print(ret, id(ret)) # ['Dylan'] 4344392712
ret2 = func('xiaobai')
print(ret2, id(ret2)) # ['Dylan', 'xiaobai'] 4344392712 # 如果你的默认参数指向的是可变的数据类型,那么你无论调用多少次这个默认参数,都是同一个.
面试题-1 ***
def func(a, list=[]):
list.append(a)
return list print(func(10,)) # [10]
print(func(20, [])) # [20]
print(func(100,)) # [10, 100] # 在传参的时候,如果使用默认参数传参,那么无论你调用多少次,使用的都将是同一个 id,如果你将默认参数覆盖传入,将会重新创建另一个 id.
面试题-2 ***
def func(a, list=[]):
list.append(a)
return list ret1 = func(10,)
ret2 = func(20, [])
ret3 = func(100,) # ret1 和 ret2 和 ret3 都执行完毕后,其 ret1和 ret3其实是同一个列表,那当然就是 ret3改变 ret1也跟着改变了. print(ret1) # [10, 100]
print(ret2) # [20]
print(ret3) # [10, 100]
局部作用域当中的坑
先引用,后定义的坑
# 在函数中,如果定义了一个变量,但是在定义这个变量之前对其引用了,那么解释器就会认为这是一个语法问题.
# 你应该在使用之间先定义. cont =1
def func():
print(cont)
cont = 3 func()
# 此时就会报错了,因为你是先引用后定义的
globlal 和 nonlocal 关键字
global
在局部作用域声明一个全局变量
def func():
global name # 在局部作用域中声明一个全局变量
name = 'Dylan'
print(name) # Dylan func()
print(name) # Dylan # 这样用 global 在函数里声明一下,那么在全局作用域中就可以找到局部作用域中的变量了
修改一个全局变量
cont = 1
def func():
global cont
cont += 1 print(cont) # 1 这之所以是1,是因为函数还没有执行,此时打印的是全局作用域中原有的 cont
func()
print(cont) # 2 这之所以是2,是因为,函数执行了,在函数里定义了全局变量,函数里把 cont 自加了个1,所以此时打印的 cont 就变成了2.函数内的自加改变了函数外的变量.就是因为 golbal在函数中定义了全局变量.
nonlocal
不能够操作全局变量
cont = 1
def func():
nonlocal cont
cont += 1
func() print(cont) # 报错了 SyntaxError: no binding for nonlocal 'cont' found
局部作用域:内层函数对外层函数的局部变量进行修改
def wrapper():
cont = 1
def inner():
nonlocal cont
cont += 1
print(cont) # 1
inner()
print(cont) # 2
wrapper()
函数名的运用
函数名的定义和变量的定义几乎一致,在变量的角度,函数名其实就是一个变量,具有变量的功能:可以赋值;但是作为函数名他也有特殊的功能就是加上()就会执行对应的函数,所以我们可以把函数名当做一个特殊的变量,那么接下来,我们就来研究一下这个特殊的变量。
函数的内存地址
函数名指向的是这个函数的内存地址
def func():
print('Dylan')
print(func) # 结果:<function func at 0x102020e18>其实深一步理解可得知,与其说函数名()可以执行这个函数,不如说是函数的内存地址()才是执行这个函数的关键,就好比:
a = 1
b = 2
c = a + b
print(c) # 3
a + b 并不是变量的相加,而是 两个变量指向的int对象的相加。
函数名可以赋值给其它变量
def func():
print("Dylan")
print(func)
a = func # 把函数当成一个变量赋值给另一个变量
a() # 函数调用 func()
通过变量的赋值,变量a,和变量func都指向的这个函数的内存地址,那么a() 当然可以执行这个函数了。
函数名可以当做容器类的元素
函数名就是一个变量,我的变量是可以当做容器类类型的元素的:
def func1():
print("in func1: 嘻嘻")
def func2():
print("in func2: 哈哈")
def func3():
print("in func3: 咯咯")
def func4():
print("in func4: 吱吱")
lst = [func1, func2, func3, func4]
for i in lst:
i() # 结果:
# in func1: 嘻嘻
# in func2: 哈哈
# in func3: 咯咯
# in func4: 吱吱
函数名可以当做函数的参数
变量可以做的,函数名都可以做到
def func1():
print('in func1') def func2(f): # 这相当于 func1
print('in func2') # in func2
f() #这相当于func1() # in func1 func2(func1)
函数名可以作为函数的返回值
def func1():
print('in func1') def func2(f):
print('in func2')
return f ret = func2(func1)
ret() # ret, f, func1 都是指向的func1这个函数的内存地址
小结:函数名是一个特殊的变量,他除了具有变量的功能,还有最主要一个特点就是加上() 就执行,其实他还有一个学名叫第一类对象。
Python新特性:f-strings格式化输出
f-strings 是python3.6开始加入标准库的格式化输出新的写法,这个格式化输出比之前的%s 或者 format 效率高并且更加简化,非常的好用,学完这个之后,以后再用格式化输出这就是你们唯一的选择。
简单举例
他的结构就是F(f)+ str的形式,在字符串中想替换的位置用{}展位,与format类似,但是用在字符串后面写入替换的内容,而他可以直接识别。
ame = 'Dylan'
age = 18
sex = '男'
msg = f'姓名:{name}, 年龄:{age}, 性别:{sex}' # f不区分大小写 print(msg) # 输出结果: 姓名:Dylan, 年龄:18, 性别:男
任意表达式
他可以加任意的表达式,非常方便:
print(f'{3*21}') # 63 name = 'barry'
print(f"全部大写:{name.upper()}") # 全部大写:BARRY # 字典也可以
teacher = {'name': 'Dylan', 'age': 18}
msg = f"The teacher is {teacher['name']}, aged {teacher['age']}"
print(msg) # The comedian is Dylan, aged 18 # 列表也行
l1 = ['Dylan', 18]
msg = f'姓名:{l1[0]},年龄:{l1[1]}.'
print(msg) # 姓名:Dylan,年龄:18.
插入表达式
可以用函数完成相应的功能,然后将返回值返回到字符串相应的位置
def sum_a_b(a,b):
return a + b
a = 1
b = 2
print('求和的结果为:\t' + f'{sum_a_b(a,b)}') # 求和的结果为: 3
多行 f(不常用)
name = 'barry'
age = 18
ajd = 'handsome' # speaker = f'''Hi {name}.
# You are {age} years old.
# You are a {ajd} guy!''' speaker = f'Hi {name}.'\
f'You are {age} years old.'\
f'You are a {ajd} guy!'
print(speaker) # Hi barry.You are 18 years old.You are a handsome guy!
其他细节
这里有一些注意的细节,了解一下就行。
```python
print(f"{{73}}") # {73}
print(f"{{{73}}}") # {73}
print(f"{{{{73}}}}") # {{73}}
m = 21
# ! , : { } ;这些标点不能出现在{} 这里面。
# print(f'{;12}') # 报错
# 所以使用lambda 表达式会出现一些问题。
# 解决方式:可将lambda嵌套在圆括号里面解决此问题。
x = 5
print(f'{(lambda x: x*2) (x)}') # 10
```
**总结**:f-string的格式化输出更加简洁,方便,易读。而且他的处理速度对之前的%s 或者format 有了较高的提升,所以以后尽量使用此种格式化输出。
可迭代对象和迭代器
可迭代对象
可迭代对象的定义
字面意思:
对象:python 中一切皆对象。一个实实在在存在的值,对象。
可迭代:更新迭代。重复的,循环一个过程,更新迭代每次都有新的内容,可以进行循环更新的一个实实在在的值(或对象)。
专业角度:在python中,但凡内部含有
__iter__
方法的对象,都是可迭代对象。目前学过的可迭代对象有:srt / list / tuple / dict / set / range / 文件句柄
查看对象的内部方法 dir()
该对象内部含有什么方法除了看源码,还可以通过 dir() 去判断一个对象具有什么方法。
s1 = 'Dylan'
print(dir(s1))
# 结果:
['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'capitalize', 'casefold', 'center', 'count', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'format_map', 'index', 'isalnum', 'isalpha', 'isdecimal', 'isdigit', 'isidentifier', 'islower', 'isnumeric', 'isprintable', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'maketrans', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']
dir() 会返回一个列表,这个列表中含有该对象的以字符串的形式所有方法名。
这样我们就可以判断python中的一个对象中是不是有
__iter__
方法,从而就可以判断这个对象是不是可迭代对象了:s1 = 'alex'
i = 100
print('__iter__' in dir(i)) # False
print('__iter__' in dir(s1)) # True
小结:
从字面意思来说:可迭代对象就是一个可以重复取值的实实在在的东西。
从专业角度来说:但凡内部含有
'__iter__'
方法的对象,都是可迭代对象。判断一个对象是不是可迭代对象:
'__iter__'
in dir(对象)优点:
- 存储的数据直接能显示,比较直观。
- 拥有的方法比较多,操作方便。
缺点:
占用内存。
可迭代对象是不能迭代取值的(除去索引,key 以外)
那么这个缺点有人就提出质疑了,即使抛去索引,key以外,这些我可以通过for循环进行取值呀!对,他们都可以通过for循环进行取值,其实for循环在底层做了一个小小的转化,就是先将可迭代对象转化成迭代器,然后在进行取值的。
迭代器
迭代器的定义
- 字面意思:更新迭代,器:工具,可更新迭代的工具。
- 专业角度:迭代器是这样的对象:实现了无参数的__next__方法,返回序列中的下一个元素,如果没有元素了,那么抛出StopIteration异常.python中的迭代器还实现了__iter__方法,因此迭代器也可以迭代。 ——出自《流畅的python》
- 简单来说:在 python 中,内部含有
'__iter__'
方法,并且含有'__next__'
方法的对象,就是迭代器。
判断一个对象是否是迭代器
# 方法如下:
# print('__iter__' in dir(对象) and '__next__' in dir(对象)) # 例子1:
with open('文件', encoding='utf-8',mode='w') as f:
print('__iter__' in dir(f) and '__next__' in dir(f)) # 输出结果: True
# 这说明,文件句柄是一个迭代器。 # 例子2:
o1 = 'alex'
o2 = [1, 2, 3]
o3 = (1, 2, 3)
o4 = {'name': '太白','age': 18}
o5 = {1, 2, 3}
with open('文件', encoding='utf-8',mode='w') as f: print('__iter__' in dir(o1)) # True
print('__iter__' in dir(o2)) # True
print('__iter__' in dir(o3)) # True
print('__iter__' in dir(o4)) # True
print('__iter__' in dir(o5)) # True
print('__iter__' in dir(f)) # True
#
print('__next__' in dir(o1)) # False
print('__next__' in dir(o2)) # False
print('__next__' in dir(o3)) # False
print('__next__' in dir(o4)) # False
print('__next__' in dir(o5)) # False
print('__next__' in dir(f)) # True # 这说明,上面这些数据类型(或者对象),除了文件句柄外,其它的都不是迭代器,只是可迭代对象。
可迭代对象如何转化成迭代器
iter(可迭代对象)或者 可迭代对象.__iter__()
s = 'Dylan'
obj = s.__iter__() # 或者 obj = iter(s)
print(obj) # <str_iterator object at 0x102894a90>
迭代器取值
l1 = [11, 22, 33, 44, 55, 66]
obj = iter(l1) # 把 l1列表转换为迭代器赋值给变量 obj。
print(obj) # <list_iterator object at 0x1032949e8>
print(next(obj)) # 11
print(next(obj)) # 22
print(next(obj)) # 33
print(next(obj)) # 44
print(next(obj)) # 55
print(next(obj)) # 66
print(next(obj)) # 再多一个就会报错了。StopIteration 停止迭代 # 迭代器利用 next 取值:一个 next 取对应的一个值,如果迭代器里面的值取完了,还要 next,那么就会报'StopIteration' 停止迭代的错误
while 循环模拟 for 循环机制
l1 = [1, 2, 3, 4, 5, 6]
# 1,将可迭代对象转化成迭代器。
obj = iter(l1)
# 2,利用 while 循环,next 进行取值。
while 1:
# 3,利用异常处理,终止循环。
try:
print(next(obj))
except StopIteration:
break
小结:
从字面意思来说:迭代器就是可以迭代取值的工具。
从专业角度来说:在 python 中,内部含有
'__iter__'
方法并且含有'__next__'
方法的对象就是迭代器。迭代器的优点:
节省内存。
迭代器在内存中相当于只占一个数据的空间,因为每次取值都会将上一条数据在内存中释放,加载当前的此条数据。
惰性机制。
next 一次,取一个值。
(有一个迭代器模式可以很好的解释上面这两条:迭代是数据处理的基石。扫描内存中放不下的数据集时,我们要找到一种惰性获取数据项的方式,即按需一次获取一个数据项。这就是迭代器模式。)
迭代顺的缺点:
速度慢,不能直观的查看里面的数据。
取值时不走回头路,只能一直向下取值。
l1 = [1, 2, 3, 4, 5, 6]
obj = iter(l1) for i in range(2):
print(next(obj)) for i in range(2):
print(next(obj)) # 迭代器会记住当前程序最后一次取值的位置,下次取值时,会继续上次的位置进行取值。
可迭代对象与迭代器的对比
可迭代对象:
是一个操作方法比较多,比较直观,存储数据相对少(几百万个对象,8G 内存是可以承受的)的一个数据集。
应用:
当你侧重于数据可以灵活处理,并且内存空间足够,将数据集设置为可迭代对象是明确的选择。
迭代器:
是一个非常节省内存,可以记录取值位置,可以直接通过循环+next 方法取值,但是不直观,操作方法比较单一的数据集。
应用:
当你的数据量过大,大到足以撑爆你的内存或者你以节省内存为首选因素时,将数据集设置为迭代器是一个不错的选择。
记录我的 python 学习历程-Day11 两个被忽视的坑、补充知识点、函数名的应用、新版格式化输出、迭代器的更多相关文章
- Python迭代器(函数名的应用,新版格式化输出)
1. 函数名的运用 你们说一下,按照你们的理解,函数名是什么? 函数名的定义和变量的定义几乎一致,在变量的角度,函数名其实就是一个变量,具有变量的功能:可以赋值:但是作为函数名他也有特殊的功能 ...
- 记录我的 python 学习历程-Day02-while 循环/格式化输出/运算符/编码的初识
一.流程控制之--while 循环 循环就是重复做同一件事,它可以终止当前循环,也可以跳出这一次循环,继续下一次循环. 基本结构(基本循环) while 条件: 循环体 示例 # 这是一个模拟音乐循环 ...
- 记录我的 python 学习历程-Day13 匿名函数、内置函数 II、闭包
一.匿名函数 以后面试或者工作中经常用匿名函数 lambda,也叫一句话函数. 课上练习: # 正常函数: def func(a, b): return a + b print(func(4, 6)) ...
- 记录我的 python 学习历程-Day12 生成器/推导式/内置函数Ⅰ
一.生成器 初识生成器 生成器的本质就是迭代器,在python社区中,大多数时候都把迭代器和生成器是做同一个概念. 唯一的不同就是: 迭代器都是Python给你提供的已经写好的工具或者通过数据转化得来 ...
- 记录我的 python 学习历程-Day03 数据类型 str切片 for循环
一.啥是数据类型 我们人类可以很容易的分清数字与字符的区别,但是计算机并不能呀,计算机虽然很强大,但从某种角度上看又很傻,除非你明确的告诉它,1是数字,"汉"是文字,否则它是分 ...
- 记录我的 python 学习历程-Day06 is id == / 代码块 / 集合 / 深浅拷贝
一.is == id 用法 在Python中,id是内存地址, 你只要创建一个数据(对象)那么就会在内存中开辟一个空间,将这个数据临时加载到内存中,这个空间有一个唯一标识,就好比是身份证号,标识这个空 ...
- 记录我的 python 学习历程-Day05 字典/字典的嵌套
一.字典的初识 为什么要有字典 字典与列表同属容器型数据类型,同样可以存储大量的数据,但是,列表的数据关联性不强,并且查询速度比较慢,只能按照顺序存储. 什么是字典 先说一下什么叫可变与不可变的数据类 ...
- 记录我的 python 学习历程-Day08 文件的操作
文件操作的初识 用 python 代码对文件进行各种操作. 基本构成: 文件路径:path 打开方式:读.写.追加.读写.写读-- 编码方式:utf-8 / gbk / gb2312-- f = op ...
- 记录我的 python 学习历程-Day03 列表/元组/rang
一.列表初识 列表是 Python 的基础数据类型之一,它是以''[ ]''的形式括起来的,每个元素用","隔开,属于容器型数据类型,他可以存放大量的.各种类型的数据. 基本格式 ...
随机推荐
- AT2346 No Need
atcoder上的题目 链接 一道思维题目 可以发现如果X是可有可无的,那么所有小于X的数也一定是可有可无的, 所有我们只要找出最大的那个可有可无的数字就好了 进一步分析,发现 若A1, A2, . ...
- ArcMap影像纠偏
客户给了一张PNG图,如下图,需要提取其中北极航线. 策略是ArcMap中先配准PNG,使之与底图较好重合.再新建线图层,描出航线.这种彩色丰富的图,很难用栅格转矢量的方式,故应用描边法. 一.配准P ...
- maven修改版本号
1.修改版本 mvn versions:set -DnewVersion=xxx 2.回滚版本,提交后不能回滚 mvn versions:revert 3.提交版本变更 mvn versions:co ...
- react-cli
更新日志: v1.2.0 ...未完待续 v1.1.0 添加editorconfig 配置ESLint 集成prettier 集成 lint-staged 实现细节: 添加editorconfig e ...
- 算法导论笔记:18B树
磁盘作为辅存,它的容量要比内存大得多,但是速度也要慢许多,下面就是磁盘的的结构图: 磁盘驱动器由一个或多个盘片组成,它们以固定的速度绕着主轴旋转,数据存储于盘片的表面,磁盘驱动器通过磁臂末尾的磁头来读 ...
- @loj - 2288@「THUWC 2017」大葱的神力
目录 @description@ @solution@ @data - 1@ @data - 2@ @data - 3@ @data - 4@ @data - 5@ @data - 6@ @data ...
- HZOJ 那一天她离我而去
一个数据水到不行的题,各路大佬用各种方法A掉了这个题(比如A*,最短路,dfs……). 这里只说一下我的暴力和被碾压的正解. 暴力AC系列: 要找过1点的最小环,那么这个环可以拆成两部分,与1相连的两 ...
- @hdu - 6427@ Problem B. Beads
目录 @description@ @solution@ @accepted code@ @details@ @description@ 有 m 种不同颜色的珠子,颜色分别为 1~m,每一种颜色的珠子有 ...
- POJ2976 题解 0/1分数规划入门题 二分
题目链接:http://poj.org/problem?id=2976 关于 0/1分数规划 参见 这篇博客 实现代码如下: #include <cstdio> #include < ...
- "?:"在正则表达式中什么意思
“?:”非获取匹配,匹配冒号后的内容但不获取匹配结果,不进行存储供以后使用. 单独的“?”:匹配前面的子表达式零次或一次. 当“?”紧跟在任何一个其他限制符(*,+,?,{n},{n,},{n,m}) ...