第三天 函数 三元运算 lambda表达式 内置函数 文件操作

时间:2023-03-09 00:42:49
第三天 函数 三元运算 lambda表达式 内置函数 文件操作

  面向过程:

直接一行一行写代码,遇到重复的内容复制黏贴。

不利于代码阅读

代码没有复用

面向对象

将代码块定义为函数,以后直接调用函数

增强了复用性

函数的定义方法

def 函数名(传递参数):

函数体

return

1、def关键字,创建函数

2、函数名

3、():

4、 函数体

5、返回值return

函数必须先定义,之后调用。不能反过来

在执行函数体的过程中,一旦执行到return,则会立即退出函数,return之后的语句将不会被执行

当定义一个函数后,执行的时候会按照函数体进行执行,并且可以将函数体的return赋值给变量

def f1():

函数体

return 111

r = f1()

print(r)   会显示111

但是如果函数体中没有写return,python会自动给函数体返回一个默认值就是None

在执行函数的时候使用

try:

函数体

expect:

return False

else:

return True

会判断函数体的执行结果,如果遇到错误就会执行expect里的内容,否则会执行else,返回值可以自定义

函数的参数

 

形式参数 在定义函数时,为了能够让调用时可以输入参数而设计的参数

实际参数 在调用函数时传递的参数

def f1(形式参数)

函数体

r = f1(实际参数)

调用时传递的实际参数数量需要与定义时定义的形式参数数量一致,形式参数可以在同一个函数中定义多个,实际参数的传递顺序也需要遵循形式参数的顺序

集中传递参数的方式

函数有四类类参数

1、普通参数,按顺序复制

2、默认参数,在定义函数时定义,但是在定义时python要求默认参数必须放在形式参数列表的最后

  

默认参数 在定义形式参数时可以定义默认参数

def f2(xx,yy,zz="ok")

其中zz就是默认参数,在调用这个函数的时候,可以传递三个参数也可以仅传递2个参数,当三个参数时zz等于第三个参数,当两个参数时zz等于默认值"ok"

3、指定参数,在调用函数时指定赋值参数的名字

指定参数必须在调用函数时放到最后,但是不允许重复赋值给同一个参数

指定参数,默认情况下传递参数的顺序是要严格按照形式参数的顺序的,但是通过在调用时指定参数也可以改变顺序

def f3(xx,yy,zz)

f3(yy="11",zz="ok",xx="ye")

def f1(arg1,arg2,arg3)

pass

错误的指定参数

f1(arg1 = 2,arg2 =3,4) # 这是不行的

f1(4,arg2=3,arg1 =2) # 这也不行,因为相当于给arg1先赋值4 后赋值2 不允许重复赋值一个参数

f1(4,arg2=3,arg3 = 2) # 正确

4、动态参数 定义形式参数时,按正常模式应该是定义多少个参数就能使用多少个参数,但是可以使用*来转换参数,这样调用函数时传递的实际参数就不再有数量限制,而传递进的多个参数将会显示为元组的格式

def f3(*xx):

  print(type(xx))

  for x in xx:

    print("x= %s " % x)

f3("aa","bb",33,44)

结果

<class 'tuple'>

x=aa

x=bb

x=33

x=44

传递的多个参数就是一个元组

这种方式有几种情况

当传递的实际参数是一个列表,那么传递进去后,会将这个列表作为元组中的第一个元素

def f3(*xx):

  print(xx)

  print(type(xx))

  for x in xx:  

    print("x= %s " % x)

li=["aa","bb",33,44]

f3(li,"xx")

结果

(['aa', 'bb', 33, 44], 'xx')  # li的全部列表作为参数的第一个元素,xx作为第二个元素

<class 'tuple'> # 整个参数变为一个列表

x=['aa', 'bb', 33, 44]          # 在执行for循环时值执行了列表被完整的输出了

x=xx

当传递的实际参数本身也加了*,则会将传递的列表内的元素拆出转换为元组中的元素

def f3(*xx):

  print(xx)

  print(type(xx))

  for x in xx:

    print("x=%s"%x)

li=["aa","bb",33,44]

f3(*li,"xx")               # 传递的列表前有一个星号

结果

('aa', 'bb', 33, 44, 'xx')

<class 'tuple'>

x=aa

x=bb

x=33

x=44

x=xx

当传递一个带星的字符串时,这个字符串里的每一个字母也会被转化为元组的元素

def f3(*xx):

  print(xx)

  print(type(xx))

  for x in xx:

    print("x=%s"%x)

f3(*"alix")

('a', 'l', 'i', 'x')

<class 'tuple'>

x=a

x=l

x=i

x=x

当传递2个*号时,传递的参数为字典类型

def f3(**xx):

  print(xx)

  print(type(xx))

  for x in  xx:

    print("x= %s " % x)

f3(kk=11,aa=22)

输出

{'aa': 22, 'kk': 11}

<class 'dict'>

x=aa

x=kk

如果传递的参数自身是一个字典,则需要将这个传递的参数前加2个星号

def f3(**xx):

  print(xx)

  print(type(xx))

    for x in xx:

      print("x=%s"%x)

ss={'aa':22,'kk':11}

f3(**ss)

{'aa': 22, 'kk': 11}

<class 'dict'>

x=aa

x=kk

5、万能参数(*args,**kwargs):在设置形式参数时,同时制定两种动态参数,这样传入的普通参数会作为函数中元组的元素,而传入的字典,则会被**的参数转换为字典

def f4(*args,**kwargs):

  print(args)

  print(kwargs)

f4("aa","bb","cc",kk=123,ss="bbb")

输出

('aa', 'bb', 'cc')

{'ss': 'bbb', 'kk': 123}

万能参数会自动识别不同类型的动态参数,同时因为字典还是元组都允许为空,所以即便传递的参数并未按照函数创建的全部顺序输入,仍然可以创建对应的空元组或空字典

def f4(*args,**kwargs):

  print(args)

  print(kwargs)

f4(kk=123,ss="bbb") # 只传递字典格式

输出

() # 元组自动被识别为空

{'kk': 123, 'ss': 'bbb'}

万能参数在设置形式参数时必须是单个星号在前,2个星号在后,也就是必须是(*args,**kwargs)

针对函数的几点补充:

1、根据解释器读取的顺序,当出现重名函数时,最后一次出现的函数将会被执行,因为之前的函数会被之后出现的函数替换加载进内存中。

def f1():

retrun a+b

def f1():

retrun a*b

a = f1(3,4)

最终的结果,相乘的函数被执行。

2、当调用函数时,传递的并不是一个复制的值,而是合格参数的地址,因此当我们传递了一个列表给函数时,函数对这个列表做的任何操作,都将直接作用于这个列表本身。

def f1(a):

a.append(99)

list = [11,22,33]

f1(list)

print(list)

最终的结果是[11,22,33,99]

3、全局变量,定义在函数外的变量可以被任何函数引用,因此称为全局变量。而定义在函数内的变量只能被本函数引用,因此称为局部变量。全局变量应该全大写,在局部变量中如果想要更新全局变量,需要使用global,但是如果全局变量是字典或列表,则只可以修改,不可以重新赋值。

修改字符串,需要用global声明变量

NAME = "LIUBO"

def f1()

global NAME

NAME = "ZHANGSAN"

f1()

print(NAME)

修改字典

li={"a":11}

def chli():

  li["b"]=22

chli()

print(li)

输出

{'a': 11, 'b': 22}

修改列表

li=[11,22]

def chli():

  li.append(22)

chli()

print(li)

输出

[11, 22, 22]

上面的例子,按正常来说函数中变更的变量仅在函数内有效,但是因为使用了global的方式宣告了这个是全局变量,因此后面对NAME的修改就会应用到全局变量中

三元运算,三目运算

为真的处理  if 条件 else 为假的处理

print(1)if 1==2 else print(0)

相当于

if 1 == 2:

  print(1)

else:

  print(0)

条件为真 打印1 ,条件为假打印0

a = 1 if 1 == 1 else 2

条件为真时 a = 1 ,条件为假时 a = 2

等价于

if 1 == 1:

a = 1

else:

a = 2

lambda表达式,对于简单的函数可以直接使用lambda表达式定义

def f1(a1,a2):

return a1+a2

上面的函数等价于

f1 = lambda a1,a2: a1+a2

def f1(a1,a2=9)

return a1+a2+100

可以写为

f1 = lambda a1,a2=9 : a1+a2+100

f1(9)    #因为函数定义了默认参数,因此只需要传递一个参数即可

lambda表达式仅能使用一行定义,因此只能定义简单函数,即函数中不能在加入其它的操作,直接返回

内置函数

abs() 取绝对值

n = abs(-1)

print(n)

输出为1

all() 所有为真,才为真

在布尔函数中表示False的内容有0,None,“”,[],{}

即空的字符串,列表,字典 元祖都是False

all([11,22,33]) 这样为真

all([11,22,“”) 这样就为假了

any() 有真就是真

ascii()  自动执行对象的_repr_方法

bin()  接收一个十进制数字转为二进制

oct() 接收一个十进制数字转为八进制

hex() 接收一个十进制数字转为十六进制

bool() 返回真假

bytes()

对于UTF-8编码一个汉字占3个字节

GBK编码一个汉字2个字节

字符串转换为字节类型,bytes("要转换的字符串",encoding="所用的编码类型")

print(bytes("刘",encoding="utf-8"))

print(bytes("刘",encoding="gbk"))

str()

将字节转化为字符串

str(需要转化的字节,encoding="需要的编码类型")

print(str(b'\xe5\x88\x98',encoding="utf-8"))

open()

文件操作

1、打开文件

f = open("文件名","操作模式",encoding="编码")

在打开一个文件时,这个文件里的内容是二进制的,那么二进制转换为字符串时有一个转换动作的。

open语句会使用一个默认的编码方式来读取文件,更换编码需要在open中指定

open("text.log","r",encoding="gbk")

在操作模式中有一个b(rb,xb,wb),这样相当于我们不需要python帮我们做编码转换,那么无论是我们写还是读就都需要使用二进制的数据类型传递。

也就是操作模式没有b则是字符串类型str ,只要有b则是字节型,bytes

f=open("test.log","ab")

f.write(bytes("刘博",encoding="utf-8")) # 写操作必须要加编码

f.close()

文件内容(追加)

dial-peer voice 1 voip1

dial-peer voice 1 voip2

dial-peer voice 1 voip3

dial-peer voice 1 voip4

dial-peer voice 1 voip5

dial-peer voice 1 voip6

刘博

+符号代表操作模式可读可写,但是方式有所区别

每当open一个文件时,相当于有一个指针出现在文件中,当read()时代表文件内所有的字节都被读了一次,这时指针也就随之指向到文件的最后。

可以通过seek(位置)方法来指定指针的位置。无论是使用b还是不带b的方式读取,seek都是按照字节来制定位置,如果存在汉字,一个汉字在utf8中是3个字节,如果文件只有一个汉字,但是seek(1)这样指针实际上是指到了这个汉字的第一个字节的后面。

在写入一个内容的时候,python会默认从指针的位置向后替换 原有的字符

f=open("test.log","r+")

f.seek(10)    #将指针指向第10个位置

f.write("ssss")

f.close()

结果

dial-peer sssse 1 voip1      #可以看到从第十个字符开始原来的voice前4个字母被替换为s了

dial-peer voice 1 voip2

dial-peer voice 1 voip3

dial-peer voice 1 voip4

dial-peer voice 1 voip5

dial-peer voice 1 voip6

刘博

获取当前指针的位置tell(),tell方法指定的位置永远是字节

f=open("test.log","r+")

f.seek(10)

a=f.tell()

f.close()

print(a)

打印

10

r+,a+,w+三种都是可读可写,但是只有r+可以指定指定指针的位置写入,a+是追加无论怎么指定,写入的都只能从最后追加,w+则会清空源文件

因此写入方式中r+使用的最广泛

操作模式:

r

只读

w

只写(先清空源文件,后写入)

x

python3.0新增:如果文件存在时则报错,如果不存在创建并写内容

a

追加

r+

可读可写,最常用

a+

可读可写,从最后写

w+

可读可写,但是先清空源文件

2、操作文件

通过源代码查看功能

read(),如果无参数,则读全部,如果有参数,打开方式如果有b按字节读取,无b按字符读取。

seek(),按字节指定指针位置

tell(),按字节获取当前指针位置

write(),写数据,如果打开方式中有b按字节,无b按字符写

close()

fileno() 文件描述符,即文件的数字表达方式

flush() 强刷,将内容强刷到硬盘上,当我们打开了一个文件,写入一个内容,之后做了一个操作暂停在这里(比如input要求用户输入,但用户没有输入)此时文件没有关闭。默认情况下这个内容是不会写到文件里的。此时可以通过flush方法在文件关闭前强制将内容写到文件中。

readable() 判断是否可读

seekable() 判断是否可以移动指针

readline() 仅读取一行,实际是执行完这句后,指针指向第二行开始,所以在此执行就会读取第二行

writeable() 判断是否可写

truncate()  会将指针后面的内容全部清空

for循环open之后的文件对象,循环每一行

f = open("test.log","r")

for line in f:

print(line)

3、关闭文件

f.close()

使用with的文件操作

with open("文件名") as f:

pass

上面的with代码执行完,文件自动关闭,不需要特意写close

python 2.7之后with支持打开多个文件

with open("1.log","r",encoding="utf-8") as f1, open("2.log","w",encoding="utf-8") as f2:

time = 0

for line in f1:

time += 1

if time <=10:

f2.write(line)

else:

break

上面的代码使用with,只读打开1.log,只写打开2.log,然后将1.log中前10行数据写入2.log中

上面说打在python中写入数据是从指针位置开始替换后面的,那么当我们要修改文件中不等长的数据(例如将alex替换为st)就会破坏原有的数据。

而open读取的数据默认是字符串的类型,因此我们可以使用字符串的操作。str.replace("源字符串","替换字符串")的方式替换。

逐行处理就是

with open("1.log","r",encoding="utf-8") as f1, open("2.log","w",encoding="utf-8") as f2:

for line in f1:

new_str = line.replace("alex","st")

f2.write(new_str)