kaixin
Published on 2023-02-11 / 15 Visits
0

Python函数

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区
    这样就可以命名相同的名字

全局范围:
    内置命名空间
    全局的名称空间
局部范围
    局部命名空间
        存放的名字
            在调用函数时,运行的函数体代码过程中产生的函数内部的名字
        存活期:
            在调用函数的存活,调用完毕后销毁
加载
内置命名空间 > 全局命名空间 > 局部命名空间
销毁
局部命名空间 > 全局命名空间 > 内置命名空间
名字查找的顺去(在局部命名空间)
局部命名空间 > 全局命名空间 > 内置命名空间

函数的命名空间是和类的形式一样的
先从自己找在自己的父的东西
'''