前言
提示:这里可以添加本文要记录的大概内容:
本文继续继Python小白基础(2)的后续章节部分,介绍了python后面的一些相关基础的知识,适合从零入门的小白看。比起之前,本文章节有些许难度提升,特别是嵌套、闭包和装饰器的相关内容。当然我可能会有些不严谨存在错误的地方望读者们谅解。
提示:以下是本篇文章正文内容,下面案例可供参考
一、作用域
1.命名空间
命名空间(Namespace)是从名称到对象的映射,大部分的命名空间都是通过 Python 字典来实现的。
命名空间提供了在项目中避免名字冲突的一种方法。各个命名空间是独立的,没有任何关系的,所以一个命名空间中不能有重名,但不同的命名空间是可以重名而没有任何影响。
一般有三种命名空间:
- 内置名称(built-in names), Python 语言内置的名称,比如函数名 abs、char 和异常名称 BaseException、Exception 等等。
- 全局名称(global names),模块中定义的名称,记录了模块的变量,包括函数、类、其它导入的模块、模块级的变量和常量。
- 局部名称(local names),函数中定义的名称,记录了函数的变量,包括函数的参数和局部定义的变量。(类中定义的也是)
命名空间查找顺序:
局部的命名空间 -> 全局命名空间 -> 内置命名空间。
命名空间的生命周期:
命名空间的生命周期取决于对象的作用域,如果对象执行完成,则该命名空间的生命周期就结束。
因此,我们无法从外部命名空间访问内部命名空间的对象。
作用域
作用域就是一个 Python 程序可以直接访问命名空间的正文区域。
在一个 python 程序中,直接访问一个变量,会从内到外依次访问所有的作用域直到找到,否则会报未定义的错误。
Python 中,程序的变量并不是在哪个位置都可以访问的,访问权限决定于这个变量是在哪里赋值的。
变量的作用域决定了在哪一部分程序可以访问哪个特定的变量名称。Python 的作用域一共有4种,分别是:
有四种作用域:
L(Local):
最内层,包含局部变量,比如一个函数/方法内部。
E(Enclosing)
:包含了非局部(non-local)也非全局(non-global)的变量。比如两个嵌套函数,一个函数(或类) A 里面又包含了一个函数 B ,那么对于 B 中的名称来说 A 中的作用域就为 nonlocal。
G(Global):
当前脚本的最外层,比如当前模块的全局变量。
B(Built-in):
包含了内建的变量/关键字等,最后被搜索。
规则顺序: L –> E –> G –> B。
在局部找不到,便会去局部外的局部找(例如闭包),再找不到就会去全局找,再者去内置中找。
locals()
本地(本函数)定义的所以变量都会以字典的形式存在这里
globals()
在外部定义的所有变量都会以字典的形式存在在这里
c = 100
def funl():
#声明要使用全部变量
global c
c += 2 # c=c+2
print(c)
pass
if __name__ == '__main__':
funl()
102
列表,字典,集合不用声明全局,可以直接用的
li = [1,2,3]
dic = {"name":"tom","age":"19"}
def funl():
#列表,字典,集合不用声明全局,可以直接用的
li.append(99)
print(li)
print(dic)
pass
if __name__ == '__main__':
funl()
实例的结果为👇:
[1, 2, 3, 99]
{'name': 'tom', 'age': '19'}
2.嵌套
嵌套
#! /usr/bin/python
# -*- coding: UTF-8 -*-
def add_b():
b=1
def do_global():
b=10
def do_nonlocal():
nonlocal b#b此时等于10
b=b+20#b=30
print(b)
pass
do_global()
print(b)
pass
if __name__ == '__main__':
add_b()
结果如下:
1
函数的嵌套
这个不太懂略😄
函数名的玩法
#! /usr/bin/python
# -*- coding: UTF-8 -*-
def fun():
print("i am a fun")
pass
def main():
f=fun()
print(f)
print(fun)
f()
pass
if __name__ == '__main__':
main()
函数名里面是一个内存地址
有这个内存地址的人,加个括号就运行函数了(个人理解)
#! /usr/bin/python
# -*- coding: UTF-8 -*-
def f1():
print("i am f1")
def f2():
print("i am f2")
def main():
li = [f1,f2]
dic = {"hanshu1":f1,"hanshu2":f2}
li[0]()
dic["hanshu2"]()
pass
if __name__ == '__main__':
main()
显示内容如下👇:
i am f1
i am f2
不定长参数
不定长参数——能需要一个函数能处理比当初声明时更多的参数。这些参数叫做不定长参数,声明时不会命名。基本语法如下:
def functionname([formal_args,] *var_args_tuple ):
“函数_文档字符串”
function_suite
return [expression]
加了星号 * 的参数会以元组(tuple)的形式导入,存放所有未命名的变量参数。
#!/usr/bin/python3# -*- coding: UTF-8 -*-
可写函数说明def printinfo( arg1, *vartuple ):
"打印任何传入的参数"
print ("输出: ")
print (arg1)
print (vartuple)
调用printinfo 函数
printinfo( 70, 60, 50 )
以上实例输出结果:
输出:
70
(60, 50)
二、闭包
认识闭包
出于种种原因,我们有时候需要在函数外部得到函数内的局部变量。但是,由于Python中作用域的搜索顺序("链式作用域"结构(chain scope):子对象会一级一级地向上寻找所有父对象的变量),这一点通常是无法实现的。
所以我们需要用闭包来解决这个问题,当然闭包的学习也是比较难理解的
内函数和外函数
外函数的返回值是内函数的内存地址
——个人理解这句话比较重要
#! /usr/bin/python
# -*- coding: UTF-8 -*-
#外函数
def outer(a):
b = 10
#内函数
def inner():
print(a+b)
#外函数返回值是内函数的内存地址
return inner
def main():
a = outer(5)
print(a)
#加括号执行
a()
if __name__ == '__main__':
main()
结果如下:
<function outer.<locals>.inner at 0x0000024EB5BCD828>
15
判断内函数是否是闭包
#判断inner是否是闭包,如果是就返回cell…,不是则返回none,
print(inner.closure)
继续按照以上实例
#! /usr/bin/python
# -*- coding: UTF-8 -*-
#外函数
def outer(a):
b = 10
#内函数
def inner():
print(a+b)
#判断inner是否是闭包,如果是就返回cell...,不是则返回none,
print(inner.__closure__)
#外函数返回值是内函数的内存地址
return inner
def main():
a = outer(5)
print(a)
#加括号执行
a()
if __name__ == '__main__':
main()
实例运行结果:
(<cell at 0x00000276B2F83588: int object at 0x00007FFCCCD67190>, <cell at 0x00000276B2F8C738: int object at 0x00007FFCCCD67230>)
<function outer.<locals>.inner at 0x00000276B327D828>
15
所以可以在外函数判定某一个内函数是不是闭包
一个闭包嵌着一个闭包
def wrapper():
money = 1000
def func():
name = "tom"
def inner():
print(name,money)
return inner
return func
def main():
f = wrapper()#此时f是func的内存地址
i = f()#此时i是inner的内存地址
i()
if __name__ == '__main__':
main()
结果为:
tom 1000
闭包的传参
#! /usr/bin/python
# -*- coding: UTF-8 -*-
def func(a,b):
def inner(x):
return a*x+b
return inner
def main():
#此时f是inner的内存地址,并且此时inner里面的4*x+5,等待传输x
f = func(4,5)
print(f(2))
if __name__ == '__main__':
main()
以上实例结果为:
13
三、装饰器
1.基本的装饰器用法
说白了就是闭包的玩法
装饰器(Decorators)是 Python 的一个重要部分。简单地说:他们是修改其他函数的功能的函数。他们有助于让我们的代码更简短
首先先回顾一下闭包相关的代码👇
import time
def fun1():
time.sleep(3)
print("i am fun1")
def timer(fun):
def inner():
#获取最初时间
start = time.time()
fun()
#结束时间-开始时间
print(time.time()-start)
return inner
def main():
a = timer(fun1)
a()
if __name__ == '__main__':
main()
实例结果为:
i am fun1
3.0139474868774414
装饰器,在运行装饰器装饰的函数的时候,会把目标函数放到装饰器函数里运行
把上面的实例里面加入装饰器
不带参数的装饰器
#! /usr/bin/python
# -*- coding: UTF-8 -*-
import time
def timer(fun):
def inner():
#获取最初时间
start = time.time()
fun()
#结束时间-开始时间
print(time.time()-start)
return inner
#而装饰器,会在运行装饰器装饰的函数的时候,会把目标函数放到装饰器函数里运行
@timer
def fun1():
time.sleep(3)
print("i am fun1")
def main():
fun1()
if __name__ == '__main__':
main()
结果为↓:
i am fun1
3.0043251514434814
带参数的装饰器
#! /usr/bin/python
# -*- coding: UTF-8 -*-
import time
def timer(fun):
def inner(a):#闭包函数需要携带参数
#获取最初时间
start = time.time()
fun(a)#执行的时候也需要参数
#结束时间-开始时间
print(time.time()-start)
return inner
#而装饰器,会在运行装饰器装饰的函数的时候,会把目标函数放到装饰器函数里运行
@timer
def fun1(a):
time.sleep(a)
print("i am fun1")
print(a)
def main():
fun1(5)
if __name__ == '__main__':
main()
运行结果为:
i am fun1
5
5.014192819595337
包含多种参数的装饰器
#! /usr/bin/python
# -*- coding: UTF-8 -*-
import time
def timer(func):
def inner(*args,**kwargs):
func(args,kwargs)
return inner
@timer
def fun1(*args,**kwargs):
print(args,kwargs)
pass
def main():
fun1('a','b',1,2,3,name="tom",age="19")
if __name__ == '__main__':
main()
(('a', 'b', 1, 2, 3), {'name': 'tom', 'age': '19'}) {}
补充:与装饰器无关的内容↓
打印函数中的注释内容
打印函数名
2.多种装饰器
装饰器传参
四、迭代器
迭代首先要想什么东西可以遍历呢
迭代是Python最强大的功能之一,是访问集合元素的一种方式。
迭代器是一个可以记住遍历的位置的对象。
迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退。
迭代器有两个基本的方法:iter() 和 next()。
字符串,列表、元组、集合或者字典都可用于迭代器。
创建一个迭代器
把一个类作为一个迭代器使用需要在类中实现两个方法 iter() 与 next() 。
如果你已经了解的面向对象编程,就知道类都有一个构造函数,Python 的构造函数为 init(), 它会在对象初始化的时候执行。
iter() 方法返回一个特殊的迭代器对象, 这个迭代器对象实现了 next() 方法并通过 StopIteration 异常标识迭代的完成。
next() 方法(Python 2 里是 next())会返回下一个迭代器对象。
创建一个返回数字的迭代器,初始值为 1,逐步递增 1
例子
#! /usr/bin/python
# -*- coding: UTF-8 -*-
def main():
li = [1,2,3,4,5,]
li_iter = li.__iter__() #开始使用迭代器迭代
item1 = li_iter.__next__()#迭代一个
print(item1)
item2 = li_iter.__next__()
print(item2)
item3 = li_iter.__next__()
print(item2)
if __name__ == '__main__':
main()
运行结果如下:
1
2
2
自动迭代
#! /usr/bin/python
# -*- coding: UTF-8 -*-
def main():
li = [1,2,3,4,5,]
li_iter = li.__iter__() #开始使用迭代器迭代
while True:
try:#捕获异常,如果try里面的东西出现异常,就直接执行excepte内的内容
item = li_iter.__next__()
print(item)
except:
print("迭代结束, break")
break
if __name__ == '__main__':
main()
运行为:
1
2
3
4
5
迭代结束, break
五、生成器
一个函数执行到一半的时候就想取出数据,那么就可以用到生成器
在 Python 中,使用了 yield 的函数被称为生成器(generator)。
在调用生成器运行的过程中,每次遇到 yield 时函数会暂停并保存当前所有的运行信息,返回 yield 的值, 并在下一次执行 next() 方法时从当前位置继续运行。
跟普通函数不同的是,生成器是一个返回迭代器的函数,只能用于迭代操作,更简单点理解生成器就是一个迭代器。
调用一个生成器函数,返回的是一个迭代器对象。
#! /usr/bin/python
# -*- coding: UTF-8 -*-
def gen_fun():
a = 1
print("将a赋值")
yield a #yield为中断函数,return a,等待下一个next才会继续执行
b = 2
print("将b赋值")
yield b
pass
def main():
g1 = gen_fun()
print(next(g1))
if __name__ == '__main__':
main()
实例的结果如下:
将a赋值
1
#! /usr/bin/python
# -*- coding: UTF-8 -*-
import time
def gen_fun():
a = 1
print("将a赋值")
yield a #yield为中断函数,return a,等待下一个next才会继续执行
b = 2
print("将b赋值")
yield b
pass
def main():
g1 = gen_fun()
print(next(g1))
time.sleep(2)
print(next(g1))
if __name__ == '__main__':
main()
将a赋值
1
将b赋值
2
自动生成
#! /usr/bin/python
# -*- coding: UTF-8 -*-
import time
def produce():
for i in range(100):
yield "生产了%s个包子"%i
pass
def main():
p = produce()
num = 0
for i in p:#此时的p.__next__()就是i
print(i)#相当于调用了yield
num += 1
if num==12:
break
if __name__ == '__main__':
main()
实例结果如下:
生产了0个包子
生产了1个包子
生产了2个包子
生产了3个包子
生产了4个包子
生产了5个包子
生产了6个包子
生产了7个包子
生产了8个包子
生产了9个包子
生产了10个包子
生产了11个包子
或者也可以方法二这样:
直接使用p.__next__()
#! /usr/bin/python
# -*- coding: UTF-8 -*-
import time
def produce():
for i in range(100):
yield "生产了%s个包子"%i
pass
def main():
p = produce()
num = 0
for i in range(12):
print(p.__next__())#相当于调用了yield
if __name__ == '__main__':
main()