深浅拷贝
a=[[1,2],1,2,3]
b=a.copy()
就是把a的值赋给b
按常理说,我修改a或b,对另一个变量不会有任何影响
但是!
如果进行下列操作
b[0][0]=3
则a输出的结果是[[3,2],1,2,3]
这就是传说中的浅拷贝(只拷贝了第一层)
为了理解这一原理,我们需要掌握一点关于内存的知识
variable1=1这一赋值操作,是先在内存中给予数字1一个内存地址(一般比较小的整数都有固定地址的,这里把1作为一个例子),再把这个内存地址赋予变量variable1
若在进行variable2=variable1的赋值,则是把variable1得到的内存地址赋予变量variable2
但是对于列表来说,比如a=[1,2],则是生成一个列表的内存地址赋予变量a,在列表的内存地址里赋予数字1和2的内存地址从而达到从列表取数的能力
回到我们发现的问题,b[0][0]=3这一操作,b[0]的内存地址没有变(和a[0]一样),但是b[0]里的b[0][0]的内存地址变了(被赋予了数字3的内存地址),而a和b的内存地址也同样没发生变化,所以我们调用a[0][0],它的内存地址和b[0][0]是一样的,即a[0][0]=3
如果是
a=[[1,1,2,3]
b=a.copy()
b[0]=4
根据之前的解答再分析一便,b[0]的内存地址变了,但是影响不到a[0],所以a依旧是[1,1,2,3]
那怎么深拷贝呢?
import copy
a=[[1,2,3],1]
b=copy.deepcopy(a)
b[0][1]=8
a的输出结果依旧是原来的!这就是深拷贝
集合set
把不同的元素(不重复的成员)组合在一起
s=set('wawa')
print(s)
输出结果为{'w','a'}
s=set(['wawa','dada','wawa'])
print(s)
输出结果为{'wawa','dada'}
set最大的作用就是去重
set内元素要求可哈希(即不可修改!),列表和字典这两种数据类型就不满足
然而set这一数据类型是非哈希的,他不能作为字典的键(key)
set操作
s.add(),相当于列表的append
s.update(),用法和add不同,是把update内的数据类型拆分成单个元素放入集合,比如update('123'),添加的是‘1’,‘2’,‘3’。update(['a','b','ccc']),添加的是'a','b','ccc'
s.pop(),随机删除里面一个元素
s.clear(),把元素清空
s.intersection(s2),两个集合求交集,等同于s&s2,注意,这不是对集合s的修改(下面union和difference都一样)
s.union(s2),两个集合求并集,等同于s|s2
s.difference(s2),求s里面有,s2里面没有的集合,等同于s-s2
s.symmertic_difference(s2),对称差集,s和s2相互没有的集合的总和,等同于s^s2
s.issuperset(s2) 查看s是否为s2的真父集,等同于s>s2
s.issubset(s2) 查看s是否为s2的真子集,等同于s<s2
set中and,&和or,|之间的关系
首先声明,and!=&, or!=|
因为and 和 or 是逻辑运算符用于判断正误,而&和|是位运算符用于并集或交集
所以set1 & set2输出的是他俩的并集,|同理
但是对于逻辑运算符:
and只有两边都是True的才会输出True结果如果我们把and前后的True分别叫做True1和True2,输出的其实是最后的那个True2。如果and两边都是真值(有值就算是真值,a=1,a=[1,2,3],True都算真值),则输出的也和前面情况一样,输出后面的真值,所以
set1 and set2==set2
or,如果or前面的值为True,则结果一定为True,就不会管后面的值了,对于真值也一样,如果是set1 or XXX (XXX为任意数据类型)输出的结果一定是set1
函数
减少重复代码,方便修改,更易修改,保持代码一致性+
使用方法
def function_name():
print('ok')
function_name()
输出结果为ok
注意,如果只写function_name不加括号,输出的是此函数所被赋予的内存地址
函数名写法准则
1. 按照函数功能命名(要有描述性)
2. 必须以下划线和字母开头,后面包含任意字母数字下划线(和变量命名一样))
函数的参数
一般函数都是带参数的
def function_name(parameter1,parameter2):
print(parameter1+parameter2)
function_name('a','b')
输出结果为ab
或者可以用形参=实参的方式
def function_name(parameter1,parameter2):
print(parameter1+parameter2)
function_name(parameter1='a',paramter2='b')
如果有些参数一般不需要改动,可以设置默认参数,想改的时候再加(注意,默认参数一定要在非默认参数后面)
def function_name(parameter1,parameter2='b'):
print(parameter1+parameter2)
function_name('a')
输出结果为ab
如果想给函数加一组不定长的无命名参数,就在参数前面加一个星号*
def function_name(*parameters):
print(parameters)
function_name(1,2,3,4,5)
输出的是一个元组(1,2,3,4,5)
如果想给函数加一组不定长的有命名的参数,就在参数前面加两个星号**,猜猜这个参数是什么数据类型?
答:字典!
def function_name(*parameters1,**parameters2):
print(parameters1,parameters2)
function_name(1,2,3,4,num1=1,num2=2)
输出结果为(1,2,3,4),{'num1':1,'num2':2}
注意,对于不定长参数的位置*parameters得放左边,**parameters得放右边
那如果要再加一个默认参数呢?
def a(a='wa',*b,**c):
print(a,b,c)
a(1,2,3,aa=1,cc=2)
输出结果为
1 (2, 3) {'aa': 1, 'cc': 2}
很明显1作为默认参数a的替代把原来的'wa'给替换掉了
然而默认参数不能放在**的后面,否则报错
总体顺序为
def function(a,b=1,*c,**d)
函数的返回值
返回某个对象给调用者function()
def function():
return None
如果函数末尾不加return,系统默认添加了return None来结束函数
return作用
1.结束函数
2.返回某个对象
def function(*a):
Sum=0
for i in a:
Sum+=i
return Sum
print(function(1,2))
输出结果为3
return也可以返回多个对象,以元组形式给予
作用域
python的作用域分4种情况:
L:local,局部作用域,函数中定义的变量
E:enclosing,嵌套的父级函数的局部作用域,但不是全局的,比如
def outer():
outer_para=1#enclosing变量
def inner():
inner_para=2#local变量
G:global,全局变量,就是模块级别定义的变量,比如a=0,b=1
B:built-in,系统固定模块里面的变量,比如int,sum
LEGB,越往右层越浅,即调用一个变量,先从B找起,再依次GEL
如果上层作用域已经定义了一个变量,下层作用域不能修改这个变量
我们会遇到这个情况
a='global'
def f():
print(a)
a='local'
f()
这组代码运行的时候会报错,因为在函数中print(a)这一步已经把a作为全局变量,再把a作为一个局部变量赋值就产生了报错问题,这是因为函数中有对局部变量a的赋值,所以程序默认整个函数内的a都是局部变量,然而print(a)前面没有任何给局部变量a赋值的语句,因此导致报错
但是对于下面代码
a='global'
def f():
a='local'
print(a)
f()
print(a)
输出结果为
local
global
因为在函数f中,a一开始就在函数中(仅在函数中)定义为一个局部变量并赋值为‘local’,而外部的全局变量a是不会受影响的。
所以牢记,全局变量和局部变量重名,局部变量可以调用但只能在函数内部调用且必须先赋值,全局变量不会因为局部变量的赋值而改变
如果想要函数内部的某个变量作为全局变量的赋值,也可以在函数内部给相应变量赋值前添加语句
global a
若要想要嵌套函数子级的某个变量作为父级变量赋值,也可以在函数内部给相应变量赋值前添加语句
nonlocal a
函数的递归
函数也可以调用自身,这样我们便可以达到递归的效果
例:
编写一个阶乘函数
def fac(n):
return n*fac(n-1)
自己调用自己并赋值进行下一步递归,看起来很不错,但是停不下来,所以我们再给它加一个条件
def fac(n):
if n==1
return 1
return n*fac(n-1)
便得到了一个用于阶乘的函数
关于递归的特性:
1.调用自身函数
2.有一个结束条件
但凡是递归可以实现的,循环也可以
但是递归相对循环来说不过灵活,很容易把事情变麻烦
内置函数
all()
判断元组,列表内是否有空元素,若有则输出False
bool()
判断一个表达式是False还是True
enumerate()
遍历,详情可看python day5
eval()
把字符串变成代码
frozenset()
不可变集合,就是不能增删改的set
几种重要的内置函数
filter()
filter(函数,可迭代对象)
序列的每个元素作为参数传递给函数进行判断,然后返回 True 或 False,最后将返回 True 的元素放到新列表中。
def ff(n):
return n<3
print(filter(ff,[1,2,3,4,5]))
然而filter是一个迭代器,输出结果为
<filter object at 0x0000022EC66BB128>
这样可以节省内存
可以list()以下把filter的数据转化为list即可
也可以用循环来把内部元素一个一个拿出来
map()
map(函数,可迭代对象),把可迭代对象内元素一个一个置入函数并处理,并把return值返回
def ff(n):
return n=3
print(map(ff,[1,2,3]))
输出结果为[4,5,6]
reduce()
reduce(函数,可迭代对象) 用传给 reduce 中的函数 function(有两个参数)先对集合中的第 1、2 个元素进行操作,得到的结果再与第三个数据用 function 函数运算,最后得到一个结果。
reduce函数需要先调用
from functools import reduce
def ff(x,y):
return x+y
print(map(ff,[1,2,3]))
输出结果为6,是通过1+2=3,再3+3=6
lambda
lambda 变量:表达式
lambda是一种比较简单的定义函数的方法
cal=lambda x,y:x+y
print(cal(1,2))
输出结果为3