Python编程笔记(第三篇)【补充】三元运算、文件处理、检测文件编码、递归、斐波那契数列、名称空间、作用域、生成器

时间:2023-03-09 00:16:02
Python编程笔记(第三篇)【补充】三元运算、文件处理、检测文件编码、递归、斐波那契数列、名称空间、作用域、生成器

一、三元运算

三元运算又称三目运算,是对简单的条件语句的简写,如:

简单条件处理:

  if 条件成立:
val = 1
else:
val = 2

  

改成三元运算

  
  val = 1 if 条件成立 else 2

  

二、智能检测文件编码

用第三方模块chardet

首先要安装chardet模块 ,用pip命令进行安装

chardet的用法

  
  import chardet
f = open("staff_table.txt","rb")
data =f.read()
f.close()
res = chardet.detect(data) #直接使用chardet模块中的detect方法
print(res)

  

输出结果

  
  {'encoding': 'utf-8', 'confidence': 0.938125, 'language': ''}

  

分析:这里结果直接给出一个encoding是判断的结果,confidence是结果对的概率

三、模拟文件修改的方式

模拟修改文件中字符的方法

1、修改文件占硬盘,即在硬盘中修改

  import os
f = open("test.txt","r+",encoding="utf-8")
g = open("new_test.txt","w",encoding="utf-8")
old_str = "的"
new_str = "地"
for line in f:
if old_str in line:
line = line.replace(old_str,new_str)
g.write(line)
f.close()
g.close()
os.replace("new_test.txt","test.txt") #注意在Windows平台不能直接重命名一个文件用os.replace()
#或者
#os.remove("test.txt") #注意在Windows平台不能直接用os.rename重命名一个文件为已经存在的文件的名字,必须先删除原文件
#os.rename("newtest.txt","test.txt")

  

2、在内存中修改

Python编程笔记(第三篇)【补充】三元运算、文件处理、检测文件编码、递归、斐波那契数列、名称空间、作用域、生成器

  
  f = open("test.txt","r+",encoding="utf-8")
data =f.read()
data = data.replace("hello","xxxx")
#由于f.read()读取文件将光标移动到文件内容的末尾,直接再次写入的会成追加模式
f.seek(0) #需要重新将光标移动到开始,
f.truncate() #由于修改后的字符串可能会比之前的字符串长度短,直接写入会有之前的字符在新写入的后面
#这里需要用文件的truncate()方法对文件内容进行清空
f.write(data)
f.close()

  

四、修改文件名的方式

1、在mac\linux系统中,直接用os.rename()方法即可

  
  import os
os.rename("new_test.txt","test.txt")

  

2、在windows系统中,不能直接用os.rename()方法,会报错,但是可以用os.replace()方法,效果与os.rename()方法一样

  
  import os
os.replace("test.txt","new_test.txt")

  

五、可变参数组

1、*args用作传递非命名键值可变长参数列表(位置参数),之后不要加普通的参数

  
  def send_msg(msg,*args,age):
print("信息是",msg,args,age)

send_msg("你好,我的信息是",*["nicholas","male"],22)

  

这样写会报错,错误提示“age”缺少一个参数,因为*args会接收实参里的22,造成age没有参数。

这样写可以

  
  def send_msg(msg,*args,age):
print("信息是",msg,args,age)

send_msg("你好,我的信息是",*["nicholas","male"],age=22)

  

这样写不会报错,但是一般写程序不会这样写。

五、尾递归优化

递归过程中,假如说求1000!的阶乘,会出现栈溢出的问题,因为在函数执行中,每次调用一个函数都会把当前函数的调用位置和内部变量保存在栈里面,由于栈的空间不是无限大(python一般为1000层深度),假如说调用层数过多,就是出现栈溢出的情况。

这个时候就可以用尾递归优化来解决,尾调用的概念非常简单,一句话就能说清楚,就是指递归函数函数的最后一步是调用这个函数本身。

例子

  
  def jc(n):
print(n)
return jc(n +1)
jc(1)

  

尾调用由于是函数的最后一步操作,所以不需要保留外层函数的调用记录,因为调用位置、内部变量等信息都不会再用到了。所以尾递归优化可以有效的防止栈溢出,但是尾递归优化需要编译器或者解释器的支持,但是大多数编程语言没有针对尾递归做优化,Python解释器也没有做优化,C语言针对尾递归做了优化,做完优化和for循环执行一样,但是python没做优化,所以即使把上面的jc()函数是尾递归方式,也会出现栈溢出。

六、eval()、exec()

eval可以执行字符串形式的表达式,并返回计算结果。

例子

  
  print(eval("1+2+3"))

  

输出结果

  
  6

  

exec()函数将字符串str当成有效的Python表达式来执行,不返回计算结果

  
  exec('print("hello world !")')

  

输出结果

  
  hello world !

  

这里的内外层双引号不能相同,否则会报错

例子

  
  code = '''
def func():
print('你好')
return 'nicholas'
v = func()
print(v)
'''
exec(code)

  

输出结果

  
  你好
nicholas

  

例子

  
  code = '''
def func():
print('你好')
return 'nicholas'
v = func()
print(v)
'''
res = exec(code)
print(res)

  

输出结果

  你好
nicholas
None

  

exec()不返回计算结果。

七、callable()

判断一个东西是否可调用,可被调用指的是对象能否使用()括号的方法调用。

例子

  
  li = [1,2,3]
print(callable(li))

  

输出结果


 False

  

例子2

  
  def func():
print("hello")
print(callable(func))

  

输出结果

  
  True

  

八、递归练习

题目

如何猜数字,猜0到100中间的一个数字,写出猜出的过程。

用二分法和递归来写

  
  li = [i for i in range(100)]
def search(num,data):
num_max = len(data)
num_mid = int(( num_max) / 2)
if num > data[num_mid]:
print("smaller")
print("列表的首位是%s,末尾是%s"%(data[0],data[-1]))
return search(num,data[num_mid:])
elif num < data[num_mid]:
print("bigger")
print("列表的首位是%s,末尾是%s" % (data[0], data[-1]))
return search(num,data[:num_mid])
else:
print("find it")
search(88,li)

  

九、名称空间

又名name space, 顾名思义就是存放名字的地方,存什么名字呢?举例说明,若变量x=1,1存放于内存中,那名字x存放在哪里呢?名称空间正是存放名字x与1绑定关系的地方

名称空间共3种,分别如下

  • locals: 是函数内的名称空间,包括局部变量和形参

  • globals: 全局变量,打印这个程序所有的变量

  • builtins: 内置模块的名字空间

不同变量的作用域不同就是由这个变量所在的命名空间决定的,有了名称空间才有了作用域

作用域即范围

  • 全局范围:全局存活,全局有效

  • 局部范围:临时存活,局部有效

查看作用域方法 globals(),locals()

十、作用域的查找顺序

LEGB 代表名字查找顺序: locals -> enclosing function -> globals -> builtins

  • locals 是函数内的名字空间,包括局部变量和形参

  • enclosing 外部嵌套函数的名字空间

  • globals 全局变量,函数定义所在模块的名字空间

  • builtins 内置模块的名字空间

十一、列表生成式+三元运算

列表生成式和三元运算可以混合使用

例子

  
  li = [i if i < 3 else i*i for i in range(6)]
print(li)

  

输出结果

  
  [0, 1, 2, 9, 16, 25]

  

例子

  
  li = [i if i != "a" else i*2 for i in "nicholas"]
print(li)

  

输出结果

  
  ['n', 'i', 'c', 'h', 'o', 'l', 'aa', 's']

  

分析:这里的for i in var ,var可以是字典、列表,也可以是字符串

十二、用两种方法来写斐波那契数列

求第n个斐波那契数列元素是多少

方法一:递归

  
  def fib(n):
if n == 0 or n == 1:
return n
else:
return fib(n-1)+fib(n-2)
print(fib(10))

  

方法二:循环的方法

  
  def fib2(n):
a,b=0,1
count = 1
while count < n:
a,b = b,a+b
count = count + 1
return b
print(fib2(10))

  

十三、isinstance()

可以使用isinstance()判断一个对象是否是Iterator对象(迭代器)或者Iterable是否可迭代(可迭代对象):

例子

  from collections import Iterable
li2 = (i for i in range(10)) #生成器表达式
dic = {"k1":"v1"}
li = [1,2,3]
s = "nicholas"
n = 2
print(isinstance(li2,Iterable))
print(isinstance(dic,Iterable))
print(isinstance(li,Iterable))
print(isinstance(s,Iterable))
print(isinstance(n,Iterable))

  

输出结果

  True
True
True
True
False

  

分析:字典、列表、字符串都是可迭代对象,这里的生成器就是迭代器,

迭代器一定是可迭代对象,可迭代对象不一定是迭代器,如这里的字典、列表等需要用__iter__()方法才能变成迭代器。

例子2

  from collections import Iterator
li2 = (i for i in range(10)) #生成器表达式
dic = {"k1":"v1"}
li = [1,2,3]
s = "nicholas"
n = 2
print(isinstance(li2,Iterator))
print(isinstance(dic,Iterator))
print(isinstance(li,Iterator))
print(isinstance(s,Iterator))
print(isinstance(n,Iterator))

  

输出结果

  
  True
False
False
False
False

  

分析:这里只有生成器表达式才是迭代器,既包含__iter__()方法又包含__next__()方法。