python第三章函数
第三章、三元运算、文件处理、函数
三元运算
三元运算又称三目运算,是对简单的条件语句的简写,如:
简单条件语句:
if 条件成立:
val = 1
else:
val = 2
# 可以写成如下:
val = 1 if 条件成立 else 2
文件处理
读,写,修改
-
读
f = open(file='d:/*.txt', mode='r', encoding='utf-8') date = f.read() f.close()
-
r是只读模式,rb是以二进制读文件
第三方模块 chardet检测二进制内容可能是什么编码
使用pip安装第三方库,py3就用pip3 install chardet
[图片上传失败...(image-9970b8-1529681325214)]
import chardet
chardet.detect(data)
{'encoding':'GB2312','confidence':0.823045,'language':'Chinese'}
-
循环文件
f = open("*.txt", "r", encoding="gbk") for line in f: print(line) f.close()
print每次执行之后会自动换行
-
写文件
-
模式 w 写模式,wb二进制写模式
-
f.write("路飞".encoding("gbk"))
以wb模式写的时候需要指定以什么编码写
-
w写模式会先清空文件,然后去写,相当于直接覆盖文件原有的内容
-
w就是一个创建模式
-
-
追加
- a 追加模式,,ab二进制追加模式
-
读写混合模式
-
r+ 读写模式,先读后写,读完可以追加.
f.read()读完之后会将指针指向文件内容末尾,再次执行f.read()就读到是空
-
w+写读模式,先写后读,先往里覆盖写,再去读你写的内容.(相当于先创建了一个空文件替换掉原来的,然后可以读这次新写的内容,几乎用不到)
-
-
文件操作的其他功能方法
def fileno(self, *args, **kwargs): 返回文件句柄在内核中的索引值,以后做IO多路复用时可以用到 def flush(self, *args, **kwargs): 把文件从内存buffer里强制刷新到硬盘 def readable(self, *args, **kwargs): 判断是否可读 def readline(self, *args, **kwargs): 只读一行,遇到\r or \n为止,指针会指向下一行开头. def seek(self, *args, **kwargs): 把操作文件的光标移到指定位置 *注意seek的长度是按字节算的, 字符编码存每个字符所占的字节长度不一样。 如“路飞学城” 用gbk存是2个字节一个字,用utf-8就是3个字节,因此以gbk打开时,seek(4) 就把光标切换到了“飞”和“学”两个字中间。 但如果是utf8,seek(4)会导致,拿到了飞这个字的一部分字节,打印的话会报错,因为处理剩下的文本时发现用utf8处理不了了,因为编码对不上了。少了一个字节 def seekable(self, *args, **kwargs): 判断文件是否可进行seek操作 def tell(self, *args, **kwargs): 返回当前文件操作光标位置,按字节单位 def truncate(self, *args, **kwargs): 按指定长度截断文件 *指定长度的话,就从文件开头开始截断指定长度,不指定长度的话,就从当前位置到文件尾部的内容全去掉。模式必须可写 def writable(self, *args, **kwargs): 判断文件是否可写
-
f.tell() 返回当前光标位
-
f.seek(0) 将光标移到文件开头
f.tell()和f.seek(0)都是以字节为单位
f.read(1)是以字符为单位,gbk中一个中文字符2个字节,utf-8中一个中文字符3个字节
-
修改文件
f = open("兼职白领学生空姐模特护士联系方式utf8.txt",'r+',encoding="utf-8") f.seek(6) f.write("[路飞学城]") f.close()
王心[路飞学城]9 46 1381234424 马纤羽 深圳 173 50 1374423423 乔亦菲 广州 172 52 1582342525 罗梦竹 北京 175 49 1862343421 刘诺涵 北京 170 48 1862343765 岳妮妮 深圳 177 54 1883524553 贺婉萱 深圳 174 52 1893344452 叶梓萱 上海 171 49 1804242324 杜姗姗 北京 167 49 13324523342 black girl 河北 167 50 13542342233
-
确实从第3个字开始改的,但是[路飞学城] 把后面的内容覆盖了
练习题
练习题1 —— 全局替换程序:
-
写一个脚本,允许用户按以下方式执行时,即可以对指定文件内容进行全局替换
`python your_script.py old_str new_str filename`
-
替换完毕后打印替换了多少处内容
# -*- coding:utf-8 -*-
# !/usr/bin/env python
# Author:dc0012
import sys
old_str = sys.argv[1] # 接受被替换文本参数
new_str = sys.argv[2] # 接收新文本
file_name = sys.argv[3] # 接收需要处理的文件名字
f = open(file_name, "r+", encoding="utf-8")
content = f.read().replace(old_str, new_str) # 替换后的文件全部内容给conten
f.seek(0) #将指针移到文件开头
f.write(content) # 将替换后的文件内容写入文件
f.truncate(f.tell()) # 防止新内容比旧内容少,将多出来的截取出去
f.close()
例: python your_script.py 北京 陕西 f_test
将f_test文件中的北京替换为陕西.
练习题2 —— 模拟登陆:
- 用户输入帐号密码进行登陆
- 用户信息保存在文件内
- 用户密码输入错误三次后锁定用户,下次再登录,检测到是这个用户也登录不了
# -*- coding:utf-8 -*-
# !/usr/bin/env python
# Author:dc0012
import sys
with open("lock_account", "r+") as f_lock:
with open("account", "r") as f_account:
count = 0
_username = input("username:").strip()
for i in f_lock:
if _username == i.strip():
print("This account was locked....")
sys.exit()
for i in f_account:
username, password = i.strip().split(",")
if _username == username:
while count < 3:
_password = input("password:")
if _password == password:
print("Welcome %s login..." % _username)
sys.exit()
else:
print("Wrong password...")
count += 1
continue
else:
print("You have tried too many times.This username will be locked")
f_lock.write(_username + "\n")
sys.exit()
print("No such this username....")
sys.exit()
account存放用户名密码一行为一个用户,用","隔开用户名密码,例:dc,123
lock_account存放被锁定的用户名,一行一个用户名.
函数
定义:
函数是指将一组语句的集合通过一个名字(函数名)封装起来,要想执行这个函数,只需调用其函数名即可
特性:
- 减少重复代码
- 使程序变的可扩展
- 使程序变得易维护
-
语法定义
def sayhi():#函数名 print("Hello, I'm nobody!") sayhi() #调用函数
-
带参数
#下面这段代码 a,b = 5,8 c = a**b print(c)
#改成用函数写 def calc(x,y): res = x**y return res #返回函数执行结果 c = calc(a,b) #结果赋值给c变量 print(c)
函数参数
参数可以让你的函数更灵活,不知能做死的动作,还可以根据调用时传参的不同来决定函数内部的执行流程
形参变量:
- 只有在被调用时才分配内存单元,在调用结束时,即刻释放所分配的内存单元.因此形参只在函数内部有效.函数调用结束返回主调用函数后则不能再使用该形参变量.
实参:
- 可以是常量,变量,表达式,函数等.无论实参是何种类型的量,在进行函数调用时,它们都必须有确定的值,以便把这些值传给形参.因此应预先赋值,输入等办法使参数获得确定值.
[图片上传失败...(image-1deb4d-1529681325214)]
默认参数:
- 在定义函数的时候赋予参数一个默认的值,如果调用的时候不给这个参数赋值,它就会调用默认的参数进行处理
默认参数必须放在位置参数后面
定义默认参数要牢记一点:默认参数必须指向不变对象!比如:如果默认参数是个空列表,而一旦更改了这个默认空列表的值,在下次调用时就会以更改后的列表作为参数来调用.
关键参数:
- 正常情况下,给函数传参数要按顺序,不想按顺序就可以用关键参数,只需指定参数名即可(指定了参数名的参数就叫关键参数),*但记住一个要求就是,关键参数必须放在位置参数(以位置顺序确定对应关系的参数)之后*
赋值的时候指定对象赋值的参数叫关键参数
def stu_register(name, age, course='PY' ,country='CN'):
print("----注册学生信息------")
print("姓名:", name)
print("age:", age)
print("国籍:", country)
print("课程:", course)
调用可以这样
stu_register("王山炮",course='PY', age=22,country='JP' )
但绝不可以这样
stu_register("王山炮",course='PY',22,country='JP' )
当然这样也不行
stu_register("王山炮",22,age=25,country='JP' )
这样相当于给age赋值2次,会报错!
非固定参数:
若你的函数在定义时不确定用户想传入多少个参数,就可以使用非固定参数
def stu_register(name,age,*args): # *args 会把多传入的参数变成一个元组形式
print(name,age,args)
stu_register("Alex",22)
#输出
#Alex 22 () #后面这个()就是args,只是因为没传值,所以为空
stu_register("Jack",32,"CN","Python")
#输出
# Jack 32 ('CN', 'Python')
还可以有一个**kwargs
def stu_register(name,age,*args,**kwargs): # *kwargs 会把多传入的参数变成一个dict形式
print(name,age,args,kwargs)
stu_register("Alex",22)
#输出
#Alex 22 () {}#后面这个{}就是kwargs,只是因为没传值,所以为空
stu_register("Jack",32,"CN","Python",sex="Male",province="ShanDong")
#输出
# Jack 32 ('CN', 'Python') {'province': 'ShanDong', 'sex': 'Male'}
- **kwargs 接收关键字参数,自动转换为字典.
如果定义函数的参数前出现了*,那么这个函数传递的参数就可以不再是固定个数,传过来的所有参数打包成一个元组
>>> nums = [1, 2, 3] >>> calc(*nums) 14
*nums
表示把nums
这个list的所有元素作为可变参数传进去。这种写法相当有用,而且很常见。
函数返回值
函数外部的代码要想获取函数的执行结果,就可以在函数里用return语句把结果返回
注意
- 函数在执行过程中只要遇到return语句,就会停止执行并返回结果,so 也可以理解为 return 语句代表着函数的结束
- 如果未在函数中指定return,那这个函数的返回值为None
函数永远只能返回一个值,这个值可以是一个元组,一个列表,一个字典
局部变量
- 在函数中定义的变量称为局部变量,在程序的一开始定义的变量称为全局变量。
- 全局变量作用域是整个程序,局部变量作用域是定义该变量的函数。
- 当全局变量与局部变量同名时,在定义局部变量的函数内,局部变量起作用;在其它地方全局变量起作用。
在函数里修改全局变量
- 在函数内部使用global 变量名 ,来声明此变量为全局变量,进行修改
一般不建议在函数内部更改全局变量
- 字典,列表,集合,对象,类里面的对象是可以在函数内部直接修改的
- 全局变量如果是字符串,整型的话,是不能在函数内部直接更改的.
函数小结:
- 定义函数时,需要确定函数名和参数个数;
- 如果有必要,可以先对参数的数据类型做检查;
- 函数体内部可以用
return
随时返回函数结果; - 函数执行完毕也没有
return
语句时,自动return None
。 - 函数可以同时返回多个值,但其实就是一个tuple。
嵌套函数
- 函数内部可以再次定义函数
- 执行需要被调用
age = 19
def func1():
# global age 定义全局变量后就不会出错
def func2():
print(age)
func2()
age = 73
func1()
# 这种调用会导致出错
作用域
- python中一个函数就是一个作用域
- 局部变量放置在其作用域中
- 定义完成后,作用域已经生成,作用域链向上查找
匿名函数
声明一个匿名函数
lamdba x, y: x*y # 声明一个匿名函数
-
lambda只支持简单的逻辑,最多支持三元运算语法
def func1(x, y): if x < y: return x * y else: return x / y # 上面的函数换成lambda就是下面的格式 func2 = lambda x, y: x * y if x < y else x / y
-
和其他方法搭配使用,用完就丢弃了
作用:
- 节省代码量
- 看着高级
高阶函数
变量可以指向函数,函数的参数能接收变量,那么一个函数就可以接收另一个函数作为参数,这种函数就称之为高阶函数.
只需要满足以下任意一个条件,即是高阶函数:
- 接收一个或多个函数作为输入
- return返回另外一个函数
递归函数
递归特性:
- 必须有一个明确的结束条件
- 每次进入更深一层递归时,问题规模相比上次递归都应有所减少
- 递归效率不高,递归层次过多会导致栈溢出(在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧,由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出)
先举个简单的例子:计算1到100之间相加之和;通过循环和递归两种方式实现
#!/usr/bin/env python3
# 1-100 sum
import sys
def sum_cycle(n):
'''
1 to n,The sum function
'''
sum = 0
for i in range(1,n + 1):
sum += i
return sum
def sum_recu(n):
'''
1 to n,The sum function
'''
if n > 0:
return n + sum_recu(n - 1) #调用函数自身
else:
return 0
print("循环求和:",sum_cycle(100))
print("递归求和:",sum_recu(100))
执行结果:
[root@localhost tmp]# ./sum.py
循环求和: 5050
递归求和: 5050
递归函数的优点是定义简单,逻辑清晰。理论上,所有的递归函数都可以写成循环的方式,但循环的逻辑不如递归清晰。
***使用递归函数需要注意防止栈溢出。在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出。
把上面的递归求和函数的参数改成10000就导致栈溢出!
[root@localhost tmp]# ./sum.py
循环求和: 5050
Traceback (most recent call last):
File "./sum.py", line 23, in <module>
print("递归求和:",sum_recu(1000))
File "./sum.py", line 19, in sum_recu
return n + sum_recu(n - 1)
File "./sum.py", line 19, in sum_recu
return n + sum_recu(n - 1)
File "./sum.py", line 19, in sum_recu
return n + sum_recu(n - 1)
[Previous line repeated 994 more times]
File "./sum.py", line 18, in sum_recu
if n > 0:
RecursionError: maximum recursion depth exceeded in comparison
***解决递归调用栈溢出的方法是通过尾递归优化,事实上尾递归和循环的效果是一样的,所以,把循环看成是一种特殊的尾递归函数也是可以的。
尾递归优化:http://www.open-open.com/lib/view/open1480494663229.html
二分法查找大家应该听说过;就是一种快速查找的方法,时间复杂度低,逻辑简单易懂,总的来说就是不断的找出中间值,用中间值对比你需要找的实际值;若中间值大,则继续找左边;若中间值小,则继续找右边;可以看出二分法就是不断重复此上过程,所以就可以通过递归方式来实现二分法查找了!
小结
- 使用递归函数的优点是逻辑简单清晰,缺点是过深的调用会导致栈溢出。
- 针对尾递归优化的语言可以通过尾递归防止栈溢出。尾递归事实上和循环是等价的,没有循环语句的编程语言只能通过尾递归实现循环。
- Python标准的解释器没有针对尾递归做优化,任何递归函数都存在栈溢出的问题。
#!/usr/bin/env python3
#The binary search function
def Binary_Search(data_source,find_n):
if len(data_source) >= 1: #判断列表长度是否大于1,小于1就是一个值
mid = int(len(data_source)/2) #获取列表中间索引;奇数长度列表长度除以2会得到小数,通过int将转换整型
if find_n > data_source[-1]: #判断查找值是否超出最大值
print('{}查找值不存在!'.format(find_n))
exit()
elif find_n < data_source[0]: #判断查找值是否超出最小值
print('{}查找值不存在!'.format(find_n))
exit()
if data_source[mid] > find_n: #判断列表中间值是否大于查找值
#print('查找值在 {} 左边'.format(data_source[mid]))
Binary_Search(data_source[:mid],find_n) #调用自己,并将中间值左边所有元素做参数
elif data_source[mid] < find_n: #判断列表中间值是否小于查找值
#print('查找值在 {} 右边'.format(data_source[mid])) #调用自己,并将中间值右边所有元素做参数
Binary_Search(data_source[mid:],find_n)
else:
print('找到查找值',data_source[mid]) #找到查找值
else:
print('{}查找值不存在!'.format(find_n)) #特殊情况,返回查找不到
Data = [22,12,41,99,101,323,1009,232,887,97]
Data.sort() #列表从小到大排序
Binary_Search(Data,323) #查找323
执行结果:
[root@localhost tmp]# ./binary_search.py
找到查找值 323
内置方法
函数名 | 作用 |
---|---|
abs() | 取一个数的绝对值 |
dict() | 把一个数据转换成字典类型 |
help() | 帮助 |
min() | 取一组整型数据中最小的 |
max() | 取最大数 |
seattr() | |
all() | 检查所有元素是不是都是True,否则返回False,用all来判断空列表会返回True |
any() | 检查所有元素只要有一个是True就返回True,判断空列表会返回False |
dir() | 打印当前程序中存在的所有变量 |
hex() | 转换16进制 |
next() | 执行迭代器..... |
slice() | 切片,class slice(start, stop[, step]) |
divmod() | divmod(10,3)返回商和余数 |
sorted() | sorted(d.items(), key = lambda x:x[1], reverse=True)可以定义排序规则 |
ascii() | 转换为unicode编码 |
oct() | 转换8进制 |
staticmethod() | |
bin() | 转换二进制 |
evel() | 按解释器规则把字符转转换为代码运行,只能处理单行代码,可以拿到运行结果 |
exec() | 按解释器规则把字符转转换为代码运行,可以执行多行代码,拿不到函数返回值或者程序运行结果 |
isinstance() | isinstance("a", str) ---> True |
ord() | 把字符串转换为ascii码 |
sum() | 求和 |
bytearray() | 将byte类型转换为bytearray类型,可以进行内存地址修改,达到修改字符串的目的,用于更改大字符串. |
filter() | 用于过滤序列,过滤掉不符合条件的元素,返回由符合条件元素组成的新列表。 该接收两个参数,第一个为函数,第二个为序列,序列的每个元素作为参数传递给函数进行判,然后返回 True 或 False,最后将返回 True 的元素放到新列表中。 |
map() | map(lambda x: x*x, [1,2,3,4,5,6])将列表每个元素进行自乘 |
reduce() | import functools,functools.reduce() |
bytes() | |
pow() | 返回多少次方 |
print() | 屏幕输出 |