python基础回顾(一)

python基础回顾

演变

先有类,再出现对象,对象生存在内存里;

理论

1、一切都皆为对象,一切皆为对象的引用。
2、只有对名字(变量名-变量)赋值的时候,才会变更引用关系。

变量

1、变量是用一个变量名表示,变量名必须是大小写英文、数字和下划线(_)的组合,且不能用数字开头。
2、变量是在内存中开辟的一个空间,实际表示是name(字符串):name.ref(内存地址)。
3、我们通常将变量名代表变量,变量值代表关联对象。

1、类就是类型:object是祖先类型。
2、类型对象是自动生成的,有且仅有一个类型对象实例。

1
2
3
int、type(1)
>>> type(1) is int
>>> True

对象

1、对象:是类的实例。
2、存活的实例对象都有唯一的id。
3、每个实例对象都持有所属类型的指针。
4、在内存中的标准头,包括引用计数cnt、类型type、其他等。

基础数据(对象)类型:

1、不可变数据(对象)类型:string、tuple、numbers(int、float)
2、可变数据(对象)类型:list、dict

名字空间(namespace)

是从名称到对象的映射。
局部名字空间:从Fast区域按需延迟复制的,静态作用域。
全局名字空间:模块直接用dict实现的,没有使用类似Fast的机制。
1、名字(变量名)、目标对象。

  • 将变量(名字)与对象(内存里的)关联起来的容器。
  • 名字空间默认使用字典(dict)的数据结构。

2、名字(变量名)的引用必须跟目标对象的引用关联起来。

  • 简单理解:变量名(字符串)的引用关联了目标对象的引用。
  • 名字空间里实质上是存在{name.ref:obj.ref}的关系。可以通过id(name),看到obj.ref。

3、名字(变量名)关联目标对象的引用关联表就存在于名字空间这个容器。

  • 简单理解:变量名(字符串)关联了目标对象的数据结构(字典)
  • 名字空间里我们看到的是{name:obj};
  • 我们这里只考虑name(字符串):obj(目标对象),不考虑深层次的ref;

4、根据作用域的不同,分为全局名字空间和当前(局部)名字空间。—这里的当前更合适一些,当前是属于局部的;

作用域

Python 程序中可以直接访问一个命名空间的代码区域。

理论

1)根据可变对象和不可变对象;
2)根据只有赋值的时候,才会便能引用关系;
3)同一作用域内,名字总属于单一名字空间,不会因其执行顺序将其引用到不同名字空间。
4)默认形参存储在函数对象的defaults里。
5)内存空间:简单划分Fast(局部变量)和DEREF(外层嵌套,闭包)。

局部作用域里的变量—使用dis.dis模块反汇编查看作用域

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
T1 = "1" #不可变对象
T2 = [] #可变对象
###############局部环境读取##################
def change_string():
print(T1) # 读取了全局变量T1 ,按照LEGB的顺序

###############局部环境创建##################
def change_string():
T1 = "dsd" # 创建了局部变量T1 ,创建正常

###############局部环境读取,创建##################
def change_string():
print(T1) # 报错 ---------局部环境读取
# 名字只属于一个名字空间,作用域解析根据LEGB就近原则,只属于当前局部作用域(局部名字空间);
# 因此,出现歧义,报错UnboundLocalError
# 解决方法 可以把T1 使用global 声明为全局
T1 = "111" # 赋值 ----------局部环境读取

在一个作用域里面给一个变量赋值的时候,Python自动认为这个变量是这个作用域的本地变量,并屏蔽作用域外的同名的变量;

函数的参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
###############默认形参##################
def change_list(a=[]):
'''
函数生成时,a的引用默认指向change_list的属性__defaults__,通过类的属性实现记录历史状态;
函数被调用时,如果传入参数,则a的引用指向传入的参数,发生引用变更;
'''
print("初始:{}".format(a))
a.append("5")
a.append("6")
a = [] # 赋值发生a的引用变更
print("然后:{}".format(a))



if __name__ == "__main__":
print(change_list.__defaults__) # ([],)
change_list() # 初始:[] 然后:[]
change_list() # 初始:['5', '6'] 然后:[]
print(change_list.__defaults__) # (['5', '6', '5', '6'],)

函数传入参数:意味着在函数的作用域内 创建了个局部变量,发生引用变更;
函数生成时,形参名字(变量)的引用默认指向函数的defaults属性;
函数被调用时,若传入参数,则形参名字的引用指向新传入的参数,若无,仍指向defaults
函数每次调用,实际上是在新的局部作用域里,重新生成新的变量;

引用、浅copy、深copy

理论

1)根据可变对象和不可变对象;
2)根据只有赋值的时候,才会便能引用关系;

引用:指向同一个对象

1
a=5 b=a 就是引用,a、b指向同一个对象;

浅copy:只复制对象内部的已有的名字引用;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
a=["1",2,[1,2,3]]
b=a.copy() # 只复制a已有对象的名字引用;就是b[0]指向a[0] ,b[1]指向a[1] ,b[2]指向a[2]

########更改已有的名字引用,可变数据类型#########
b[2].append(4) # b[2]更改后a[2]会保持一致;---b[2]是列表(可变类型),因为没有赋值,未发生引用变更
print(a) # ['1', 2, [1, 2, 3, 7]]
print(b) # ['1', 2, [1, 2, 3, 7]]

########更改已有的名字引用,可变/不可变数据类型########
b[2]=3 # b[2]更改后a[2]不会保持一致;---b[2]是数字(不可变类型), 因为赋值,发生了引用变更
print(a) # ['1', 2, [1, 2, 3, 7]]
print(b) # ['1', 2, 3]

########更改未有的名字引用######################
b.append(0) # b添加了新的名字引用,a不会保持一致;---浅copy只能同步更改已有的名字引用
print(a) # ['1', 2, [1, 2, 3, 7]]
print(b) # ['1', 2, [1, 2, 3, 7], 0]

深copy

1
2
3
from copy import deepcopy
a=["1",2,[1,2,3]]
c=deepcopy(a) # 递归复制所有引用成员(而不是只复制引用);c和a之间是独立的,没啥关系了,不会同步;

闭包

闭包是指函数离开生成环境后,仍可记住并持续引用词法作用域的外部变量。

理论

1)根据可变对象和不可变对象;
2)根据只有赋值的时候,才会便能引用关系;
3)闭包引起的环境变量叫做自由变量,保存在函数对象的closure属性里,从内存的Fast转到DEREF里;

普通的闭包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
def func():
'''
方法返回了一个函数
函数内部需要外部的变量count
count成为自由变量,引用指向代码对象的__closure__
'''
# count = [0]
count = 5 # 不可变对象

def foo():
# count[0] = count[0] + 1
num = count + 1
# fu()
print(num)

return foo


cc = func()
print(cc.__closure__) # (<cell at 0x052A2290: int object at 0x5B43D470>,)

记住外部状态的闭包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
def func():
'''
方法返回了一个函数
函数内部需要外部的变量count
count成为自由变量,引用指向代码对象的__closure__
'''
count = [0]

# count = 5 # 不可变对象

def foo():
count[0] = count[0] + 1
# num = count + 1
# fu()
print(count)

return foo


cc = func()
print(cc.__closure__) # (<cell at 0x052A2290: int object at 0x5B43D470>,)
cc() # [1]
cc() # [2]

传递函数的闭包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

def fu():
print("inner")

def func(fun):
'''
传入的参数,创建fun指向它,并因为foo的调用,成为自由变量
方法返回了一个函数
函数内部需要外部的变量count
count成为自由变量,引用指向代码对象的__closure__
'''
count = [0]

# count = 5 # 不可变对象

def foo():
count[0] = count[0] + 1
# num = count + 1
fun()
print(count)

return foo


cc = func(fu)
print(cc.__closure__) # (<cell at 0x052A2290: int object at 0x5B43D470>,)
cc() # inner [1]
cc() # inner [2]

装饰器其实就是利用了闭包的这种方式实现的。

装饰器

原理就是利用闭包在函数里传入一个函数对象,函数对象在闭包里调用。

无参装饰器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def w1(func):
def inner():
# 验证1
# 验证2
# 验证3
return func()
return inner
#方法一:
def f1():
print 'f1'
cc=w1(f1)
cc()
#方法二:
@w1
def f1():
print 'f1'
f1()

带参装饰器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def w2(av):
def w1(func):
def inner():
# 验证1
# 验证2
# 验证3
print(av)
return func()
return inner
return w1

@w1('autor')
def f1():
print 'f1'
f1()

带参的装饰器,就是又加了一层嵌套

本文标题:python基础回顾(一)

文章作者:HT

发布时间:2018年03月27日 - 23:03

最后更新:2018年03月28日 - 13:03

原始链接:http://7ht.gitee.io/2018/03/27/python基础回顾(一)/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。