Python学习(二)

一、深拷贝和浅拷贝

对于数字和字符串而言,赋值、浅拷贝和深拷贝没有意义,永远指向同一个内存地址,其中一个变量的值改变,这个变量会指向其他内存地址,其他的变量指向的内存地址不变,即值不变

 1 import copy
 2 n1 = 123
 3 print(id(n1))
 4 n2 = n1
 5 print(id(n2))
 6 n3 = copy.copy(n1)
 7 print(id(n3))
 8 n4 = copy.deepcopy(n1)
 9 print(id(n4))
10 n1 = 456
11 print(n1)
12 print(id(n1))
13 print(n2)
14 print(id(n2))

1421199632
1421199632
1421199632
1421199632
456
1683152
123
1421199632

字符串、数字的赋值、浅拷贝和深拷贝的示意图

对于字典、元组、列表而言,对他们进行赋值、浅拷贝和深拷贝时,内存地址的变化是不同的,对于赋值两个变量的内存地址是相同的,对于浅拷贝而言,只拷贝第一层(比如字典只拷贝键,键的内存地址不是原来的,例如某一个键对应的值是一个列表,则两个变量的列表都是相同的内存地址,还是原来的内存地址,若改变其中一个列表中的值则两个变量都会改变),深拷贝则可以拷贝多层(比如字典中某一个键对应的值为一个列表,则列表的内存地址也拷贝了一份,即使是多层嵌套也同样,两个变量的列表内存地址指向不同的内存地址,若改变其中一个变量中列表的值,其他变量中列表的值不变)

 1 import copy
 2 n1 = {"k1":"wu","k2":123,"k3":["alex",456]}
 3 print(id(n1))
 4 n2 = n1
 5 print(id(n2))
 6 print('\n')
 7 n3 = copy.copy(n1)
 8 print(id(n3))
 9 print('\n')
10 print(id(n1["k3"]))
11 print(id(n3["k3"]))
12 print('\n')
13 n4 = copy.deepcopy(n1)
14 print(id(n4))
15 print('\n')
16 print(id(n1["k3"]))
17 print(id(n4["k3"]))
18 print('\n')
19 print(id(n1["k3"][0]))
20 print(id(n4["k3"][0]))

31157416
31157416


31157488


36842760
36842760


31986456


36842760
36842568


36642464
36642464

字典、元组、列表的赋值示意图

字典、元组、列表的浅拷贝示意图

字典、元组、列表的深拷贝示意图

 1 import copy
 2 dict_computer = {"cup":[80],"mem":[50],"disk":[60]}
 3 new_dict_computer1 = copy.copy(dict_computer)
 4 new_dict_computer1["mem"][0] = 80
 5 print(dict_computer)
 6 print(new_dict_computer1)
 7 print('\n')
 8 new_dict_computer2 = copy.deepcopy(dict_computer)
 9 new_dict_computer2["mem"][0] = 60
10 print(dict_computer)
11 print(new_dict_computer2)

{'cup': [80], 'mem': [80], 'disk': [60]}
{'cup': [80], 'mem': [80], 'disk': [60]}


{'cup': [80], 'mem': [80], 'disk': [60]}
{'cup': [80], 'mem': [60], 'disk': [60]}

二、函数

 把一些功能重复使用的代码封装起来,可以被调用,形成一个函数,函数的基本结构为:定义函数名,写入参数(可以不写,也可以是默认参数、动态参数),冒号下边是函数实现的功能,可以return函数结果值

1 def func(*args,**kwargs):
2     pass

函数内部的变量称为局部变量,局部变量只在函数体内有效(所以函数内部和函数外部可以有相同的变量名,互不影响),该函数体外部不能调用,函数体外部的变量称为全局变量,函数内部要访问全局变量需要在函数内部声明它是全局变量,即 global 变量名

函数体括号内写入函数变量名,称为普通参数,函数返回值可以有多个,接收时也要有多个参数,要一一对应

1 def func(a,b,c):
2     print(a+b)
3     return c,b+c
4 
5 d,e = func(1,2,5)
6 print(d)
7 print(e)

3
5
7

可以为函数设置默认参数,调用函数时,若没有传入对应的参数则函数执行默认参数,若传入对应参数则函数默认参数被替代,也可以对函数中的形参指定参数

 1 def func(a,b,c=9):
 2     print(a+b)
 3     return c,b+c
 4 
 5 d,e = func(1,2,5)
 6 print(d)
 7 print(e)
 8 print('\n')
 9 d,e = func(1,2)
10 print(d)
11 print(e)
12 print('\n')
13 d,e = func(c=1,a=2,b=7)
14 print(d)
15 print(e)

3
5
7


3
9
11


9
1
8

当不确定给函数传入多少参数时,可以对函数体的形参设置成动态参数,*arg接收的参数变为一个列表,可在函数体中读出参数列表或者循环读出一个个参数,**karg接收的参数变为一个字典,可对参数进行字典的操作

 1 def sum(*nums):
 2     print(nums)
 3     sum_num = 0
 4     for num in nums:
 5         sum_num += num
 6     return sum_num
 7 
 8 def dict_fun(**kwargs):
 9     print(kwargs)
10     print(kwargs.keys())
11     print(kwargs.values())
12     for i in kwargs.keys():
13         print(kwargs[i])
14 
15 sum_num = sum(1,2,3,4,7,8,5,4,6)
16 print(sum_num)
17 
18 list1 = [1,2,3,4,7,8,5,4,6]
19 sum_num1 = sum(*list1)
20 print(sum_num1)
21 
22 dict_fun(k1=3,k2=5,k3=7)

(1, 2, 3, 4, 7, 8, 5, 4, 6)
40
(1, 2, 3, 4, 7, 8, 5, 4, 6)
40
{'k1': 3, 'k3': 7, 'k2': 5}
dict_keys(['k1', 'k3', 'k2'])
dict_values([3, 7, 5])
3
7
5

二、lambda表达式

lambda表达式,即简单函数的简单表示,lambda表达式的格式为:

函数名 = lambda 形参: 功能

函数功能只能有一行代码,形参可以为多个,调用时格式为:函数名(实参)

1 fun = lambda a,b: a+b
2 print(fun(2,3))

5

三、内置函数

内置函数指不需要导入模块,就可以直接使用的函数,一般都是因为使用频率比较频繁或是是元操作,所以通过内置函数的形式提供出来,基本的数据操作基本都是一些数学运算(当然除了加减乘除)、逻辑操作、集合操作、基本IO操作,然后就是对于语言自身的反射操作,还有就是字符串操作。

数学运算类

 1 print(complex(2,3))   # 创建一个复数
 2 print(abs(-9))   # 求绝对值,参数可以为实数,也可以为复数,复数返回模
 3 print(divmod(9,4))  # 求商和余数,返回第一个值为商,第二个值为余数
 4 print(pow(3,2))  # 计算a的b次幂
 5 for i in range(2,8):
 6     print(i)          #  生成从a到b-1的一个序列数
 7 print(round(2.7))  # 四舍五入
 8 a = set([1,5,7])
 9 print(sum(a))
10 print(sum([1,2,3]))  # 对集合中的值求和
11 print(oct(10))  #  将一个数转化为8进制数
12 print(hex(10))  #  将一个数转化为16进制数
13 print(ord('a')) #  将字符变为ASCII码
14 print(chr(97))  #  将ASCII码变为字符
15 print(bin(20))  #  将数字转化为二进制
16 print(bool(-1)) #  转化为布尔类型,除0外都为真

enumerate()是python的内置函数,在字典上是枚举、列举的意思,对于一个可迭代的(iterable)/可遍历的对象(如列表、字符串),enumerate将其组成一个索引序列,利用它可以同时获得索引和值,enumerate多用于在for循环中得到计数

 1 ret = list(['苹果','香蕉','西瓜'])
 2 for i,item in enumerate(ret,1):   # 第一个参数为一个序列,第二个参数为起始编号,不写默认从0开始
 3     print(i, item)
 4 
 5 # 如果要统计文件的行数,可以这样写
 6 count = 0
 7 for index, line in enumerate(open('123.txt','r'),1):
 8     count += 1
 9     print(index,line)
10 print(count)

1 苹果
2 香蕉
3 西瓜
1 this is first linethis is second line

2 this is third line

2

eval()  参数为一个计算式的字符串,可将字符串中的计算结果给出:result = eval('9*8')    print(result)   -->  72;

map(function, list) 一个序列根据条件映射为另一个新的序列,可得到映射后的序列,第一个参数为条件函数,第二个参数为原始的列表

 1 def add100(x):
 2     return x + 100
 3 hh = [11,22,33]
 4 for value1 in map(add100,hh):  # 将列表中的每一个数映射到函数中,得到新的列表
 5     print(value1)
 6 
 7 def abc(a, b, c):
 8      return a*10000 + b*100 + c
 9 list1 = [11,22,33]
10 list2 = [44,55,66]
11 list3 = [77,88,99]
12 for value2 in map(abc,list1,list2,list3):  # 如果给出了多个可迭代参数,则对每个可迭代参数中的元素‘并行’应用到function
13     print(value2)

111
122
133
114477
225588
336699

filter(function, list)  一个序列根据条件过滤,符合条件的生成一个新的序列,可接收这个序列,第一个参数为条件函数,第二个参数为原始序列;和map()类似,filter()也接收一个函数和一个序列,和map()不同的是,filter()把传入的函数依次作用于每个元素,然后根据返回值是True还是False决定保留还是丢弃该元素

 1 def is_odd(n):
 2     return n % 2 == 1
 3 list1 = []
 4 for value1 in filter(is_odd, [1, 2, 4, 5, 6, 9, 10, 15]):
 5     list1.append(value1)
 6 print(list1)
 7 
 8 def not_empty(s):
 9     return s and s.strip()
10 list2 = []
11 for value2 in filter(not_empty, ['A', '', 'B', None, 'C', ' ']):
12     list2.append(value2)
13 print(list2)

[1, 5, 9, 15]
['A', 'B', 'C']

float()  把一个数字或者数字的字符串转换为浮点型;frozenset()  使集合不能进行增减等改变操作;

max()  序列的最大值;mix()  序列的最小值;id()  查看变量的内存地址;

reversed()  参数为字符串,序列,使之反转,若参数为字符串则得到反转后单个字符组成的列表;

sorted()  对序列排序,序列中要同类型数据

zip() 参数为序列,将序列中对应序号的元素各自组成一个元组,生成新的序列,zip函数接受任意多个(包括0个和1个)序列作为参数,返回一个tuple列表

 1 x = [1, 2, 3]
 2 y = [4, 5, 6]
 3 z = [7, 8, 9]
 4 for value1 in zip(x, y, z):
 5     print(value1)
 6 
 7 x = [1, 2, 3]
 8 y = [4, 5, 6, 7]
 9 for value2 in zip(x, y):
10     print(value2)
11 
12 x = [1, 2, 3]
13 for value3 in zip(x):
14     print(value3)
15 
16 x = [1, 2, 3]
17 for value4 in zip(*[x] * 3):
18     print(value4)

(1, 4, 7)
(2, 5, 8)
(3, 6, 9)
(1, 4)
(2, 5)
(3, 6)
(1,)
(2,)
(3,)
(1, 1, 1)
(2, 2, 2)
(3, 3, 3)

四、迭代器和生成器

迭代器是访问集合中元素的一种方式,从第一个元素开始直到元素结束,迭代器只能向前访问,不能向后,生成迭代器的方式是将一个序列传入迭代器函数

1 f = open('456.txt','r',encoding='utf-8')
2 file = f.readlines()
3 file_iter = iter(file)          #  将一个序列传入iter()函数,形成一个迭代器
4 print(file_iter.__next__())     #  读取迭代器的内容,用obj.__next__()方法,一次读取一个迭代器元素
5 print(file_iter.__next__())

古剑奇谭OL

剑侠情缘网络版3

一个函数被调用时返回一个迭代器,那这个被调用的函数叫做生成器,即函数中包含yield语法,那这个函数就是一个生成器

 1 def cash_money(amount):
 2     while amount>0:
 3         print('取钱!')
 4         amount -= 100
 5         yield 100             # 每执行一次obj.__next__(),函数会从上到下执行,等执行到yield这行代码
 6                               # 会返回yield后面的值,同时停止运行设为断点,当执行下一次obj.__next__()时,
 7                               # 函数从yield后面的代码执行,当执行完整个代码之后会继续从头开始执行,到yield
 8                               # 代码行返回值并设为断点,再执行下一次obj.__next__()时重复之前的行为
 9         print('取了100,又来!')
10 
11 atm_cash = cash_money(500)
12 print(type(atm_cash))
13 print(atm_cash)
14 print(atm_cash.__next__())
15 print('第一次执行obj.__next__()结束后!')
16 print(atm_cash.__next__())
17 print('第二次执行obj.__next__()结束后!')

<class 'generator'>
<generator object cash_money at 0x000000B48EA7D1A8>
取钱!
100
第一次执行obj.__next__()结束后!
取了100,又来!
取钱!
100
第二次执行obj.__next__()结束后!

举一个生产者和消费者的例子,生产者做包子,消费者吃包子

 1 import time
 2 
 3 def customer(name):
 4     print("%s准备吃包子了。。。" %name)
 5     while True:
 6         baozi = yield     # 当执行obj.__next__()时,函数运行到这行代码,等待给yield传入值,同时设置断点,
 7                           # 每当给yield传入参数,函数都会把yield赋值给变量,并执行yield下面的程序,当执行完
 8                           # 之后,不是再从头开始执行函数,而是到断点处等待下一次给yield传入参数,当再给yield
 9                           # 传入参数,则重复上一次的行为
10         print("%s吃了%s个包子。。。" %(name, baozi))
11 
12 def conductor(name1):
13     print("%s开始做包子了。。。" %name1)
14     a = customer('A')     #用生成器函数得到一个迭代器
15     b = customer('B')
16     a.__next__()       #执行迭代器函数中代码,预备读取yield中的内容
17     b.__next__()
18     for i in range(5):
19         time.sleep(1)
20         print("第%s次做了2个包子。。。" %(i+1))
21         a.send(1)    #向迭代器中的yield传入值
22         b.send(1)
23         print("第%s执行结果!" %(i+1))
24 
25 conductor("zhu")

zhu开始做包子了。。。
A准备吃包子了。。。
B准备吃包子了。。。
第1次做了2个包子。。。
A吃了1个包子。。。
B吃了1个包子。。。
第1执行结果!
第2次做了2个包子。。。
A吃了1个包子。。。
B吃了1个包子。。。
第2执行结果!
第3次做了2个包子。。。
A吃了1个包子。。。
B吃了1个包子。。。
第3执行结果!
第4次做了2个包子。。。
A吃了1个包子。。。
B吃了1个包子。。。
第4执行结果!
第5次做了2个包子。。。
A吃了1个包子。。。
B吃了1个包子。。。
第5执行结果!

五、装饰器

 装饰器是对功能进行扩展,在不改变原来的函数代码基础上,增加函数的功能,装饰器的框架如下:

def login(func):
    def inner(*args,**kwargs):
        # 功能1
        # 功能2
        # 功能3
        return func(*args,**kwargs)   # 在原来的业务函数中,有返回值的需要return才能被接收
    return inner

# 装饰函数,扩展函数功能
@login   #执行login函数,把被装饰的函数的函数名当作参数传入login,login(w1),其返回值inner(函数地址)赋予w1,得到的新的w1就执行inner中的内容和原来w1中的内容
def w1(*args,**kwargs):
    # 函数功能1
    # 函数功能2
    return value

#调用函数
value = w1(name,passwd)

当一个函数需要扩展多个功能时,需要对装饰器传入以扩展功能函数的函数名为参数的形参,这样就需要在外部再加入一层函数,装饰器框架如下:

def derector(name1,name2):    #装饰器框架,传入扩展功能函数函数名
    def need_func(func):    #装饰函数,传入业务函数函数名
        def inner(*args,**kwargs):
            result_name1 = name1(*args,**kwargs)  #执行扩展功能函数
            result_name2 = name2(*args,**kwargs)
            result_fun = func(*args, **kwargs)  # 执行业务函数
            return result_fun
        return inner
    return need_func

def num1(*args,**kwargs):   #扩展功能函数
    print(789)
def num2(*args,**kwargs):
    print(159)

@derector(num1,num2)  # 当执行derector函数,把扩展功能函数的函数名传入derector,返回need_func时把被装饰的函数名w1传入need_func,返回inner时就执行inner内部的功能
def w1(a,b):
    print(a)
    print(b)
    return 5

result = w1('123','456')
print(result)

789
159
123
456
5

posted on 2017-06-01 10:59  水涵空  阅读(204)  评论(0)    收藏  举报