函数对象
函数嵌套
名称空间与作用域
闭包函数
装饰器
练习
一 函数对象
- #1 可以被引用
- def max(x, y):
- return x if x> y else y
- func = max
- print(func(1, 2))
- #2 可以当作参数传递
- def max(x, y):
- return x if x> y else y
- def max_1(x, func):
- return func(x, 1)
- print(max_1(2, max))
- #3 返回值可以是函数
- #4 可以当作容器类型的元素
- def foo():
- print('foo')
- def bar():
- print('bar')
- dic={
- 'foo':foo,
- 'bar':bar,
- }
- while True:
- choice=input('>>:').strip()
- if choice in dic:
- dic[choice]()
二 函数嵌套
- def foo():
- def bar():
- print('from bar')
- bar()
- foo()
- bar() #报错
三 名称空间与作用域
- # 名称空间: 存放名字的地方,(x=1,1 存放于内存中, 名称空间正是存放名字 x 与 1 绑定关系的地方)
- # 三种名称空间: 局部名称空间 ---> 全局名称空间 ---> 内置名称空间
内置名称空间在 Python 解释器启动时就创建了, 直到 Python 解释器退出时内置名称空间才失效. 这使得我们可以在程序的任何位置使用内置名称空间内的名称, 例如, id(),print() 等函数.
全局命名空间, 每个模块加载执行时创建的, 记录了模块中定义的变量, 包括模块中定义的函数, 类, 其他导入的模块, 模块级的变量与常量.
python 自带的内建命名空间, 任何模块均可以访问, 放着内置的函数和异常.
#1, 作用域即范围
- 全局范围 (内置名称空间与全局名称空间属于该范围): 全局存活, 全局有效
- 局部范围 (局部名称空间属于该范围): 临时存活, 局部有效
- #2, 作用域关系是在函数定义阶段就已经固定的, 与函数的调用位置无关, 如下
- x=1
- def f1():
- def f2():
- print(x)
- return f2
- x=100
- def f3(func):
- x=2
- func()
- x=10000
- f3(f1())
- #3, 查看作用域: globals(),locals()
LEGB 代表名字查找顺序: locals -> enclosing function -> globals -> __builtins__
locals 是函数内的名字空间, 包括局部变量和形参
enclosing 外部嵌套函数的名字空间 (闭包中常见)
globals 全局变量, 函数定义所在模块的名字空间
builtins 内置模块的名字空间
四 闭包函数
它是怎么产生的及用来解决什么问题呢. 给出字面的定义先: 闭包是由函数及其相关的引用环境组合而成的实体 (即: 闭包 = 函数 + 引用环境)
- def counter():
- n = 0
- def incr():
- nonlocal n
- x = n
- n += 1
- return x
- return incr
- c = counter()
- print(c())
- print(c())
- print(c.__closure__[0].cell_contents) # 查看闭包的元素
用途: 延迟计算
- from urllib.request import urlopen
- def index(url):
- def get():
- return urlopen(url).read()
- return get
- baidu = index('http://www.baidu.com')
- print(baidu().decode('utf-8'))
五 装饰器
装饰器就是闭包函数的一种应用场景, 用于拓展原来函数功能的一种函数
强调装饰器的原则: 1 不修改被装饰对象的源代码 2 不修改被装饰对象的调用方式
装饰器的目标: 在遵循 1 和 2 的前提下, 为被装饰对象添加上新功能
装饰器函数以目标函数名为参数, 且必须返回一个函数调用目标函数参数执行
- import time
- def deco1(func):
- def wrapper(*args, **kwargs):
- start_time = time.time()
- print('deco1 start')
- func(*args, **kwargs)
- print('deco1 end')
- end_time = time.time()
- print(end_time - start_time)
- return wrapper
- def deco2(func):
- def wrapper(*args, **kwargs):
- start_time = time.time()
- print('deco2 start')
- func(*args, **kwargs)
- print('deco2 end')
- end_time = time.time()
- print(end_time - start_time)
- return wrapper
- @deco1
- @deco2
- def fun(x, y):
- time.sleep(2)
- print(x + y)
- fun(1, 2)
装饰器函数的参数为被装饰函数. 装饰后执行被装饰函数相当于执行装饰器函数返回的函数, 即例子中的 wrapper
六 练习
- # 编写函数, 函数执行的时间是随机的
- import random
- import time
- def func1():
- t = random.random()
- print('sleeping time =', t)
- print('sleeping starts')
- time.sleep(t)
- print('sleeping ends')
- func1()
- # 编写装饰器, 为函数加上统计时间的功能
- import random
- import time
- def deco(func):
- def wrapper(*args, **kwargs):
- stime = time.time()
- func(*args, **kwargs)
- etime = time.time()
- print('deco time:', (etime - stime))
- return wrapper
- @deco
- def func():
- t = random.random()
- print('sleeping time =', t)
- print('sleeping starts')
- time.sleep(t)
- print('sleeping ends')
- func()
- # 编写装饰器, 为多个函数加上认证功能, 要求登录成功一次, 在超时时间内无需重复登录, 超过了超时时间, 则必须重新登录
- # db.txt 内容:{'user': 'zsy', 'passwd': '123'}
- db = 'db.txt'
- def auth(authtype='file'):
- user_status = {'user': None, 'passwd': None}
- def origin_auth(func):
- def wrapper(*args, **kwargs):
- if authtype == 'file':
- if user_status['user'] != None or user_status['passwd'] != None:
- func(*args, **kwargs)
- else:
- dict = eval(open(db, 'r', encoding='utf-8').read())
- user_name = input('Name:')
- user_passwd = input('Passwd:')
- if user_name == dict['user'] and user_passwd == dict['passwd']:
- user_status['user'] = user_name
- user_status['passwd'] = user_passwd
- print('登录成功')
- func(*args, **kwargs)
- else:
- print('用户名或密码错误')
- elif authtype == 'sql':
- print('暂无 sql 库')
- else:
- print('错误的 authtyoe')
- return wrapper
- return origin_auth
- origin_auth = auth()
- @origin_auth
- def func():
- print('func')
- func()
- @origin_auth
- def func2():
- print('func2')
- func2()
- # 编写装饰器, 实现缓存网页内容的功能:
具体: 实现下载的页面存放于文件中, 如果文件内有值 (文件大小不为 0), 就优先从文件中读取网页内容, 否则, 就去下载, 然后存到文件中
- import os, requests, hashlib
- engine_conf = {
- 'file': {'dirname': 'db'},
- 'sql': {
- 'host': '127.0.0.1',
- 'port': 3306,
- 'user': 'root',
- 'password': '123'},
- 'redis': {
- 'host': '127.0.0.1',
- 'port': 6379,
- 'user': 'root',
- 'password': '123'}
- }
- db = 'db.txt'
- def make_cache(engine = 'file'):
- if engine not in engine_conf:
- raise TypeError(engine + 'not in engine_conf')
- user_status = {'user': None, 'passwd': None}
- def deco(func):
- if user_status['user'] == None or user_status['passwd'] == None:
- user_info = eval(open(db, 'r').read())
- user_name = input('User:')
- user_passwd = input('Passwd:')
- if user_name != user_info['user'] or hashlib.md5(user_passwd.encode('utf-8')).hexdigest() != user_info['passwd']:
- print('用户名或密码错误')
- exit()
- else:
- user_status['user'] = user_name
- user_status['passwd'] = user_passwd
- def wrapper(url):
- if engine == 'file':
- filename = hashlib.md5(url.encode('utf-8')).hexdigest()
- filepath = os.path.join(engine_conf['file']['dirname'], filename)
- if os.path.exists(filepath):
- print('已爬取该网页')
- else:
- content = func(url)
- if content == None:
- print('爬取内容为空')
- else:
- with open(filepath, 'w', encoding='utf-8') as f:
- f.write(content)
- elif engine == 'sql':
- pass
- elif engine == 'redis':
- pass
- return wrapper
- return deco
- @make_cache()
- def get_html_text(url, *args, **kwargs):
- try:
- response = requests.get(url)
- response.encoding = response.apparent_encoding
- response.raise_for_status()
- return response.text
- except:
- return None
- get_html_text('http://www.baidu.com')
来源: http://www.bubuko.com/infodetail-2751696.html