Python函数
函数时代码的集合
函数的优势:
1.将重复的代码复用
2.将代码长度减少,将代码分开,维护性高
面向过程的编程思想
也就是在编写程序时,时刻想着程序的执行过程,就是流程
好像就是流水线设计
解决的问题的方式
优点:
将复杂问题流程化,变为简单化
缺点:
扩充性非常差
面向对象的编程思想
也是基于面向过程的编写逻辑的过程
# 计算器底层就是一个流水性的过程
面向过程的编写思想应用场景
1.不是所有的软件都需要平凡的迭代: 脚本
2.及时一个软件需要迭代 ,也不软件全部的部分需要更迭,只是部分
函数式编程
将函数互相调用,实现数学的层面逻辑
1.函数的使用
1.函数的定义
def func(val):
print(val)
func('函数')
1.定义函数接收的参数为形参
2.调用函数传入的参数为实参
2.关键字传入参数
关键字传参,会根据形参的名称传入对应的参数,按号对位
def func(val):
print(val)
func(val='函数')
3.位置传参
根据实参的位置进行传入参数,对应函数形参的位置
def func(val1, val2):
print(val1)
print(val2)
func('函数', '函数2')
# 注意问题:
关键字传参和位置传参组合使用:位置传参在前,关键字在后
def func(val1, val2):
print(val1)
print(val2)
func('函数', val2='函数2')
4.默认参数
形参在定义时,已经存在
如果对形参传值,存在默认参数将会覆盖,如果没有传值使用默认参数
def func(val1, val2='函数2'):
print(val1)
print(val2)
func('函数','函数666')
5.动态参数
分为: *args,**kwargs
args 按照的是位置传参,按照元祖进行接收
**kwages 按照的关键字传参,按照的是字典结构接收
# args
def func(*args):
print(args) # 打印结果('函数', '函数666')
func('函数','函数666')
# kwargs
def func(**kwargs):
print(kwargs) # {'a': '函数', 'b': '函数666'}
func(a='函数',b='函数666')
# 组合使用(规则也是位置传参在前,关键字在后)
def func(*args, **kwargs):
print(kwargs) # {'a': '函数', 'b': '函数666'}
print(args) # ('123', 1111)
func('123', 1111, a='函数', b='函数666')
2.函数的返回值
1.存在返回值 会将返回值返回给调用者,可以赋值给变量也可以直接打印
def func(val):
print(val)
return '666'
val = func('函数')
print(val) # 666
2.不返回值 没有返回值,那么接受者接收的值就是None
def func(val):
print(val)
val = func('函数')
print(val) # None
返回值的作用:
函数中代码处理后一些自己需要用的数据,可以通过返回值获取获取函数的结果
关键字,在函数中return
函数的概念性内容
1.关于函数穿的参数是什么?
函数在传参时默认传入的是什么:传入的是内存地址。
优势:
1.省内存空间
2.函数在内部可以对数值进行处理(处理的是内存地址)
参数需要可变类型才能对参数进行修改:参数可变类型:list,dict,set
3.其他语言是对参数进行拷贝,对拷贝的进行处理。如果python也想这样需要copy模块进行拷贝,函数就会对拷贝的参数进行处理,不会对原数据进行处理
例如:引用的是当前变量的内存地址,而不是拷贝
name = '666sada'
def func(val):
print(id(val)) # 2337699345904
print(id(name)) # 2337699345904
func(name)
2.函数的返回值是什么
函数的返回值返回的是:内存地址
在函数内部有引用计数器,当使用参数时,计数器加1
函数内部的变量在当函数执行完毕后,变量自动销毁,计数器减1
返回值的特点:
1. 当两个变量接受函数的返回值,数值相同,内存地址是不同的,两个执行函数的变量,相互不会干扰。
每个变量都会进行创建内存地址。
例如:
def foo():
return [123, 45645]
v1 = foo() # 返回的内容都为[123, 45645]
print(id(v1)) # 2508241651968
v2 = foo() # 返回的内容都为[123, 45645]
print(id(v2)) # 2508241460928
2.每次执行函数,如果值时数值和字符串,那么的内存地址不论执行多少次函数都是相同的。
因为python有内存驻留机制。考虑到函数内部使用数值和字符串次数太多,为了优化内存产生。
def foo():
return 123
v1 = foo()
print(id(v1)) # 2114972440752
v2 = foo()
print(id(v2)) # 2114972440752
3.函数的默认值,在初始函数创建默认内存地址。
存在默认值
1.当函数第一次执行时,就会加载一个默认值得存储地址(只会加载一次)。
2.当函数第二次执行时,不会对默认值进行第二次内存地址的创建,只要不传参就一直使用这个默认值内存地址(进行维护)。
3.当对带有默认值函数进行传参时,就会使用传入参数的内存地址。
4.在特殊情况下,默认值时可变类型:list dict set,可以对数据进行增删。
案例1:
def func(a, b=[1, 2, 3]):
b.append(a)
return b
val1 = func(12)
print(val1) # [1, 2, 3, 12]
# 当前存在默认参数时,函数会创建对b的内存地址(创建1次),没对对b进行传入参数,那么就会一直使用默认b的内存地址
案例2
def func(a, b=[1, 2, 3]):
b.append(a)
print(id(b)) # 只要不进行覆盖一直使用的b默认值的内存地址,1911771908352
return b
val1 = func(12)
print(val1) # [1, 2, 3, 12]
val2 = func(13)
print(val2) # [1, 2, 3, 12, 13]
val3 = func(14)
print(val3)
val4 = func(15, [16]) # 2799334278464 发生覆盖默认值地址发生变化
print(val4) # [16, 15]
每一次执行函数都不会对默认值进行内存地址的重新创建,从始至终一直使用默认值得初始的内存地址。
4.动态参数高级用法
*args **kwargs
动态参数不仅仅可以当成形参,也可以当成实参进行处理
在执行函数时,可以将传入的列表字典等数据打散传入。
def func(*args,**kwargs):
print(args)
print(kwargs)
func([11,12],{"a":'666'}) # 结果就是 args ([11, 12], {'a': '666'}) kwargs {}
func(*[11,12],**{"a":'666'}) # 结果就是 args (11, 12) kwargs{'a': '666'}
可以对函数传入的参数进行打撒操作,会根据数据的结构传入args和kwargs中
元祖列表集合打撒后传入ages
字典 打撒后传入kwages
5.函数名/函数
1.
函数在执行可以成为元素,或者参数,或者返回值等等执行或者调用
将函当成返回值 or 参数 or 元素都可以
def func():
print(1)
def show():
return func
v1 = show() # v1接收show函数的返回值func(func函数的名称)
v1() # ()执行了func函数
2.
函数要可以被哈希的。所以函数可以被当成列表元素,字典的键,字典的值,集合的元素
['函数1','函数2']
3.
参数个数不同可以使用动态参数进行打散进行传递。
{'函数1':func....}
4.
函数名赋值,当函数名赋值给一个变量,这个变量就可以加上括号执行函数 v1 = func函数名 v1()执行func函数
6.print与return的区别
如果函数中没有return返回值,那么函数执行赋值给变量就会接受到一个none
print作用:帮忙输出内容文本
return作用:将函数的结果,或者其他值,返还给函数,让函数赋值打印,方便调用者的使用。同时还可以当做暂停作用,当执行到return时,不会执行下面的代码,直接结束函数。
7.作用域
分为全局和局部。
每个函数都属于一个局部作用域
全局作用域:谁都可以被调用,局部作用域可以进行读取,但是不能重新赋值,和修改。
局部作用域:只能自己内部的局部代码进行使用,其他的不可以调用,如果数据没有就去,全局中去找
name = '666' # 全局作用域变量
def func():
age = 18 # 局部作用域变量
print(name) # 在局部没有从全局找
print(age) # 报错,直接找全局不会找局部
8.global关键字
可以在函数内部对全局的作用域的值进行修改
1.不是使用global关键字:
name = '666' # 全局作用域变量
def func():
name = '777'
print(name) # 777
func()
print(name) # 666
2.使用global关键字
name = '666' # 全局作用域变量
def func():
global name
name = '777'
print(name) # 777
func()
print(name) # 777
函数高级语法
1.函数的嵌套
1.作用问题
全局作用于函数,在每一次调用都会产生一个局部的作用域(每次调用就会产生一个新的局部作用域),在函数内部还有函数,就会在父级函数中产生一个子级的函数作用域,这就叫嵌套
2.函数执行问题
当函数执行时内部有其他的函数执行,如果内部没有就去全局找并且执行。
当函数的名字相同时,下面的函数会覆盖上面的函数(python从上到下执行)
当函数执行时,确定函数作用域是谁。
函数可以在子作用域中或者更深。
例如:
def foo(): #嵌套在局部作用域
print('开始') # 4.打印
def func():
print('正在执行') # 6.打印
func() # 5.执行foo函数内嵌套的func函数
print('结束') # 7.打印
def out(): #嵌套在全部作用域
print('我在执行out函数') # 2.打印
foo() # 3.调用foo函数
out() # 1.调用当前的out函数执行
函数在寻找时,也是遵循这现在自己作用域(儿子)找,找不到再去全局(爸爸)找。全局不能去局部找
作用域总结:
1.优先找自己的作用域,没有在去上一级找
2.在作用域找值,确保值时什么
3.分析函数的执行过程,确认作用域
2.函数的闭包
是由函数的嵌套组成,将数据封装到一个函数中(一个作用域中)
def 函数名1(形参):
def 函数名2():
print(形参) 打印参数,如果自己的作用域中没有,就去父级作用域找 (函数名1,就是打印函数名1的形参)
return 函数名2
v1 = 函数名1() 将函数名2的 名字赋值给 v1变量
v1() 执行函数名2
在函数名1 全局作用域中,内部创建一个函数名2的局部作用域。
案例:
def func():
print('开始')
def show():
print('结束')
return show
v1 = func()
v1()
# 在函数内部调用函数,外层函数返回内部的函数名称 在进行执行
3.函数装饰器
关键字:@装饰器名字
批量对函数执行之前后做操作使用函数装饰器
在使用装饰器后原函数名变成一个参数传入装饰器函数中
基于@语法和函数闭包,将函数封装到闭包中,将函数赋值给新函数,执行函数时在对新函数中执行闭包中的原函数
可以在不改变原函数的代码,和调用方式前提下,实现函数的执行和扩展
多个函数系统执行前执行后自定义一些功能
实例:
def outer(origin):
def inner(*args,**kwargs):
res = origin(*args,**kwargs)
return res
return inner
@outer
def func():
pass
func()
将func 函数当成参数传入进去,在装饰器内部添加一些自定义的功能,在将主函数func返回给inner函数,在将inner返回给outre函数
结果就是
res = func()
innre = func()
inner = outer
outer = func()
相当于转到最后,在装饰器内部,转一圈下来执行了func函数
例如:
# func返回值是show 也就是执行了show函数
def func(val): # val = inner
print('开始') # 1
def show(*args, **kwargs):
print('666') # 3
res = val(*args, **kwargs)
return res # 返回的是已经执行函数inner
print('结束') # 2
return show
@func # 将被装饰的函数作为参数传入到装饰器中,开始执行装饰器函数
def inner():
print(1) # 4
inner() # 对应被装饰的函数的执行可以理解为执行了装饰func
4.函数的递归
递归是一种编程思想 算法离不开递归(算法使用)
# 特点:
1.函数内部自己调用自己
2.必须有出口
没有出口就会报错:电脑最大递归深度 900 - 1000
与函数链接起来
# 案例
def sum_numbers(num):
# 执行结束后的出口
if num == 1:
return 1
# 函数调用自己
return num + sum_numbers(num - 1)
'''
第一遍执行 num =3
return 3 + 函数(2) # 函数(2) = 结果3
第二遍执行 num = 2
return 2 + 函数(1) # 函数(1) = 1 结果1
第三遍执行 num = 1
return if num == 1 return 1
# 开始将返回值返回给函数
# 最终结果就是 6
# 主要还是return 的返回值起了最关键的作用,当函数执行到return时,函数就会停止,将结果返回给函数
'''
print(sum_numbers(3))
5.二分发查找算法概念
lis = [11, 12, 13, 14, 15, 16, 78, 99, 101, 1104, 1588]
num = 16
# 第一种方式
# 使用enumerate 获取 元素下标 元素本身
# 缺点速度太慢,如果数据量比较大的情况下,几乎需要一个一个的去对比
# for i, k in enumerate(lis):
# if k == num:
# print('end 获取成功,下标%d' % i)
# break
# 二分查找(必须是有序的)
'''
具体的逻辑
lis = [小的值,中间值,大的的值]
select_num = 需要找的值
mid_val = 列表中间的索引
start_num = 列表0索引
end_num = 列表的 -1索引
if select_num > lis[mid_val]:
# 就说明 就要取大的值
在进行重复调用
elif select_num< list[mid_val]
# 就说明 就要取小的部分的值
在进行重复调用
else :
print('打印出来当前所在的值的下标')
'''
# 具体实现的代码
def dichotomy(lis, select_num):
'''
:param lis: 有序列表
:param select_num: 查找的值
:return: 查找的值在列表中的下标索引
'''
if len(lis) == 0:
# 如果查询的值不存在列表中进行判断
print('值不存在当前列表中')
return -1
len_num = len(lis) # 总长度
mid_val = len_num // 2 # 中间索引的长度 取整数
if select_num > lis[mid_val]:
# 在列表的中间 - 尾部的数量 进行列表的切片
lis = lis[mid_val + 1:]
dichotomy(lis, select_num)
elif select_num < lis[mid_val]:
# 起始 - 在列表的中间
lis = lis[:mid_val]
dichotomy(lis, select_num)
else:
print('值存在列表中')
return 1
dichotomy(lis, 15)
# 如果列表是无序的 需要对列表进行排序
nums = [11,12,568,99,66]
nums.sort()
print(nums) # [11, 12, 66, 99, 568]
匿名函数
匿名函数:就是没有名字的函数。
关键子 lambda
格式 : lambda 接受 : 函数体
例如:lambda x:函数体
例如:lambda x1,x2:函数体
例如:lambda *args,**args:函数体
在匿名函数体设置(只是支持单行代码)内部都会有一个return,将值返回给一个接受变量
例如:
f1 = lambda x:x+100
v1 = v1(10)
print(v1) 结果:110
匿名函数和三元运算结合使用
name = lambda x :666 if x >10 else 999
n= name(参数)
print(n)
传入的参数成立 就输出 666
传入的参数不成立 就输出 999
内置函数1
abs() # 获取绝对值。负数变整数,整数变整数
pow() # 取次方 参数1 参数2
sum() # 求和,参数可以被迭代的 ,可以被循环的
v1,v2 = divmod() # 取商和余数 赋值给2个变量 参数1,参数2
round() #保留小数点后几位,参数1小数,参数2保留几位数的整数
min() # 取列表或元组中最小的值 可以别迭代的
max() # 取列表或者元组中最大的值 比较取到的是 原值
all() # 是否全部是True 转换为布尔值是不是全是True
any() # 是否存在是True 转换为布尔值是不是只要一个是True得到是True
ord() # 获取字符的10进制的代码数字
chr() # 根据数字获取相对的字符
len() # 获取值得长度
open() # 打开文件
type() # 获取数据类型
range() # 获取数值
enumerate() # 维护一个对应的 索引位置,和元素值
help() # 获取python内部的提示信息 使用手册
callable() # 判断是否可以加括号执行 返回True/False
sorted("字典,元组,集合",reverse=True) # 进行排序字典取k进行排序 可以制定字典的值进行排序 True 正序
sorted("字典.itema()",key=lambda x:x["字典的其中一个值"]["某个指的名字"])
# 字符串方法
num = '123' # 可以迭代
print(int(num)) # 123
print(str(num).__repr__()) # '123'
print(bool(num)) # True
print(float(num)) # 123.0 可以转换为字符串10进制整数类型
print(tuple(num)) # ('1', '2', '3') 不可迭代的对象不可以转为元组
# eval() 也就是将字符串类型转换为原有的类型
print(eval(num)) # 转为整形
print(eval('[1,2,3,4,5,6,]')) # 转为列表
内置函数2
dic = {
'siry': 300,
'tom': 7000,
'lili': 10000,
'jack': 2000
}
******* max min *******
max(传入可迭代对象)
取一个值进行比较,取一个值进行比较,比较最大的
min(传入可迭代对象)
取一个值进行比较,取一个值进行比较,比较最小的
print(max(dic)) # 获取的是tom ,因为字典按照key比较
# key指定一个函数
# 工作原理:max 进行迭代,会将传给key指定的函数中,key函数执行的返回值当为比较的值
# 如果使用后有名函数,那么max 就会自动调用这个用名函数,将迭代值传入
print(max(dic,key=lambda key:dic[key]))
******** sorted 排序的原理和参数 ****
# 参数1:可迭代对象 参数2:函数(处理方法)(可以不传):排序方式 reverse=Tuer 或者 Fasle(可以不传)
# 进行传入迭代器对象,进行迭代器对象的之间的对比,进行排序,如果传入的是字典那么排序按照key排序,可以指定方式
# sorted也是排序方法,传入的字典没有指定排序方法会按照key排序
# 字符串key 进行排序
# 可以传入key参数,指定排序方式
print(sorted(dic,key=lambda key:dic[key]))
****了解*****
# map
# 第一个参数传入函数(处理的规则) 第二个参数传入可迭代对象
lis = [11, 12, 13, 14, 15, 16, 78, 99, 101, 1104, 1588]
# 每一次迭代就会将元素传入给第一个参数的函数中进行处理
# 和 (lis*2 for i in lis) 元组生成性质一样 获取一个生成器
print(map(lambda key: key * 2, lis)) # 返回一个生成器
# filter 过滤方式
# 第一个参数传入函数(处理的规则) 第二个参数传入可迭代对象
l = ['xx', 'xx1', 'xx2']
# (i for i in l if i.endswith('1')) 和 filter相同
# 第二个参数做了: for i in l
# 第一个参数做了: 接受循环的参数,进行判断是否符合条件
print(filter(lambda key: key.endswith('1'), l)) # 返回生成器
# reduce 可以作为一个累加的操作
from functools import reduce
# 合并操作
# 第一个参数 一个函数 参数2 可迭代列表 参数3初始的值
# 执行流程就是: 初始值 + 被循环的第一个值 在和函数方法进行处理 获取一个结果
# 这个结果 就会和列表中循环的第二个值相加 以此类推
# 如果不传默认值,那么就是none,就会取出列表的两个值进行相加结果 和后面的值相加
print(reduce(lambda x,y:x+y,[1,2,3],10))
# 取绝对值
print(abs(-100)) # 将负数转为整数
# 四舍五入
print(round(1.3)) # 以0.5位界限进行
作用于关键字
global 关键字可以通过局部作用域对'全局变量'进行修改
nolcal 关键字通过局部作用对'上级作用域变量'进行修改
生成器
作用:生成器是由函数和yield关键字 构造出来的,在特定条件下帮助我们省去内存空间
1.生成器函数
def 函数名():
函数体
yield 数值或者内容,当添加yield关键字时就是生成器函数
2.生成器函数与函数的不同
当执行生成器函数时:不会被执行,会获得一个生成器的对象
例如:
def func()
print(123)
yield 123
print(123)
yield 123
v1 = func() 生成器对象
print(v1) 打印到生成器对象
生成器对象执行 只能用next执行
v1 = next(v1)
执行到yield时 就会停止函数的执行将yield的值返回给v1
在下一次执行时
v2= next(v1)
会接着上次v1执行的地方,向下执行,执行到yield值返回给v2
...以此类推
当执行完毕后 会报错 表示生成器函数的代码执行完毕 :stopiteration
生成器对象可以用for 循环,每一次循环都会执行next()
for执行完毕后不会报错误,直接结束。
(执行方法2)
data = func()
for i in data: 在每次循环会执行 next(data)
生成器函数执行的小案例 获取4位验证码
import random
def gen_random_num(max_count):
counter = 0
while counter < max_count:
yield random.randint(1000, 9999)
counter += 1
data_list = gen_random_num(3000000)
n1 = next(data_list)
print(n1)
深浅拷贝
深浅拷贝:
说的是可变类型 set list dict 才有意义,不可变类型无意义
一个是只拷贝父对象,一个是拷贝父对象中的子对象完全拷贝
浅拷贝:
a = {1: [1,2,3]}
b = a.copy()
a[1].append(4)
print(a,b) # {1: [1, 2, 3, 4]} {1: [1, 2, 3, 4]}
'''
a 与 b 都是独立的对象,但是都执行同一个内存地址(同一个引用)
只要其中一个改变,那么另一个就会改变
'''
深拷贝
import copy
a = {1: [1, 2, 3]}
b = copy.deepcopy(a)
a[1].append(4)
print(a, b) # {1: [1, 2, 3, 4]} {1: [1, 2, 3]}
'''
a 与 b 两个完全独立的对象,指向的也是不同的内存地址,如果a改变,那么b不会受到影响
'''
Python模块的导入
模块
模块
一系列功能的集合体
1.内置
2.第三方
3.自己写的
为何要存在
1.内置与第三方 :提升自己的开发效率 拿来主义
2.将公共代码提取出来,减少代码的编写,进行共享
import time 时发生了什么
1.执行time.py
2.产生了time.py的名称空间,将time.py运行过程中的产生的名字丢到time的名称空间中
3.在该文件中产生一个名字time,名字执行time.py产生的名称空间
import time # 产生名空间
import time # 直接引用首次导入的名称空间
import time # 直接引用首次导入的名称空间
# 引入
print(time.now) # 这是指明道姓的从一个模块中获取方法
now = 0 # 在当前文件中命名now参数 不会影响正常的引入,因为不存在一个命名空间中
# 无论产看还是修改都是以原模块为基准,与调用关系无关
导入规范
import 内置的
import 第三方
import 自定义
# 模块是第一类对象
模块的可以被赋值为变量
导入
循环导入的问题
也就是 1.py文件 导入了 2.py文件
2.py文件中 导入了 3.py
3.py文件中又导入 1.py 不会在造明细空间
将公用的内容变量方法 放到一个公共的文件夹中
模块的搜索的优先级
1.内存
将包存入内存加载
2.硬盘找
按照sys.path的文件顺序进行查找 导入的包
import sys
print(sys.path) # 文件列表(python从硬盘找到对应包的顺序)
列表的第一个元素 是第一个执行文件的文件夹
列表的第二个元素 是当前执行文件夹的父类 (ied帮助加载的,忽略,在脚本目录中是不存在的)
进行import
就会将文件内存地址存到 内存中 导入后,一直存储到内存中,直到程序结束
print(sys.modules) # 内存中已经加载的导入模块 返回的字典
# 导入的顺序
当导入文件时,如果内存没有就会从 硬盘找,将包缓存到内存中去
当再次到导入后,就会先从内存中找到对应的包
如果在内存中找到不包,同时又在硬盘中找不到就需要进行添加
# 将需要的文件模块向对路径添加到这个 python找到磁盘的列表中
import sys
sys.path.append(r'C:\Users\56515\Desktop\abc.py')
包
包是什么: 包中含有__init__.py的文件夹
包的本质是模块的一种形式,包是用来当做模块导入
将多个py文件放到包中__init__.py中
环境变量以'执行文件'为准所以被导入的模块或者后续的文件引用的sys.path都是参照执行文件的sys.path为主(谁是执行文件,那么就是以他父级模块文件夹为主)
到的是__init__.py文件 将全部的包中的功能导入到__init__.py文件中
包的文件夹必须要存在执行文件的中的sys.path中(在执行文件夹下的相同文件夹中)
作为模块(包)的使用者 要模块在环境变量中(sys.path.append('路径'))
导入
无论是使用者还是设计者 from.. import.. 还是import.. 凡是使用中带点 点的左边必须是一个包,否者非法(语法规定)
import 顶级包.子包.子模块 必须遵守这个原则(点.左边 必须是一个包 要不然报错)
A包和B下有同名的模块不会冲突,A.a 和B.a 来自于两个命名空间
import 导入文件时名称空间中的名字来源于文件本身 import 包. 产生的名称空间中的名字同样来源于文件本身 就是包中__init__.py 导包就是到文件下的init文件
绝对导入没有限制
# 相对导入(包能之间的导入 推荐使用相对导入)
# . :代表当前的文件夹(包)
# .. :代表上一层文件夹(包的上一层文件名)
# . 不能超出顶层包中(只能在包中使用不能跨出包)
命名空间
'''
将栈区的进行分片处理
a = 10 # 在1区
a = 11 # 在2区
这样就可以命名相同的名字
全局范围:
内置命名空间
全局的名称空间
局部范围
局部命名空间
存放的名字
在调用函数时,运行的函数体代码过程中产生的函数内部的名字
存活期:
在调用函数的存活,调用完毕后销毁
加载
内置命名空间 > 全局命名空间 > 局部命名空间
销毁
局部命名空间 > 全局命名空间 > 内置命名空间
名字查找的顺去(在局部命名空间)
局部命名空间 > 全局命名空间 > 内置命名空间
函数的命名空间是和类的形式一样的
先从自己找在自己的父的东西
'''