python学习点滴记录

时间:2022-04-12 22:49:38

复习:yield

1:把函数的执行结果封装好__iter__和__next__,即得到一个迭代器
2:与return功能类似,都可以返回值,但不同的是,return只能
返回一次值,而yield可以返回多次值
3:函数暂停与再继续运行的状态是有yield保存

示例

def func(count):
print('start')
while True:
yield count
count
+=1
g
=func(10)
print(next(g))
#yield的表达式形式的应用
def
eater(name): print('%s 说:我开动啦' %name)
food_list
=[]
while True:
food
=yield food_list
food_list.append(food)
#['骨头','菜汤']
print('%s eat %s' %(name,food))

alex_g
=eater('alex')
#第一阶段:初始化
next(alex_g) #等同于alex_g.send(None)
print('===========>')

#第二阶段:给yield传值
print(alex_g.send('骨头')) #1 先给当前暂停位置的yield传骨头 2 继续往下执行,直到再次碰到yield,然后暂停并且把yield后的返回值当做本次调用的返回值
#
print('===========>')
print(alex_g.send('菜汤'))
print(alex_g.send('狗肉包子'))

 面向过程:

在调用一个函数的过程中,直接或间接的调用了函数本身

见以下代码示例:

python学习点滴记录python学习点滴记录
#面向过程:核心是过程二字,过程即解决问题的步骤,基于面向过程去设计程序就像是在设计
#
一条工业流水线,是一种机械式的思维方式

#优点:程序结构清晰,可以把复杂的问题简单化,流程化
#
缺点:可扩展性差,一条流线只是用来解决一个问题
#
应用场景:linux内核,git,httpd,shell脚本

#grep -rl 'error' /dir/
import os
def init(func):
def wrapper(*args,**kwargs):
g
=func(*args,**kwargs)
next(g)
return g
return wrapper

#第一阶段:找到所有文件的绝对路径
@init
def search(target):
while True:
filepath
=yield
g
=os.walk(filepath)
for pardir,_,files in g:
for file in files:
abspath
=r'%s\%s' %(pardir,file)
target.send(abspath)
# search(r'C:\Users\Administrator\PycharmProjects\python18期周末班\day5\aaa')
#
g=search()
#
g.send(r'C:\Python27')

#第二阶段:打开文件
@init
def opener(target):
while True:
abspath
=yield
with open(abspath,
'rb') as f:
target.send((abspath,f))




#第三阶段:循环读出每一行内容
@init
def cat(target):
while True:
abspath,f
=yield #(abspath,f)
for line in f:
res
=target.send((abspath,line))
if res:break



#第四阶段:过滤
@init
def grep(pattern,target):
tag
=False
while True:
abspath,line
=yield tag
tag
=False
if pattern in line:
target.send(abspath)
tag
=True


#第五阶段:打印该行属于的文件名
@init
def printer():
while True:
abspath
=yield
print(abspath)

g
= search(opener(cat(grep('os'.encode('utf-8'), printer()))))
# g.send(r'C:\Users\Administrator\PycharmProjects\python18期周末班\day5\aaa')

g.send(r
'C:\Users\Administrator\PycharmProjects\python18期周末班')
#a1.txt,a2.txt,b1.txt
View Code

 递归的执行分为两个阶段:

1、递推

2、回溯

 示例:猜年龄

直接
def func():
print('from func')
func()
func()

间接
def foo():
print('from foo')
bar()
def bar():
print('from bar')
foo()
foo()

age(
5)=age(4)+2
age(
4)=age(3)+2
age(
3)=age(2)+2
age(
2)=age(1)+2
age(
1)=18

age(n)
=age(n-1)+2 #n>1
age(1)=18 #n=1

def age(n):
if n == 1:
return 18
return age(n-1)+2
print(age(5))

 

python学习点滴记录

 递归效率低,需要在进入下一次递归时保留当前的状态

1. 必须有一个明确的结束条件

2. 每次进入更深一层递归时,问题规模相比上次递归都应有所减少

3. 递归效率不高,递归层次过多会导致栈溢出(在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出)

python学习点滴记录python学习点滴记录
l =[1, 2, [3, [4, 5, 6, [7, 8, [9, 10, [11, 12, 13, [14, 15,[16,[17,]],19]]]]]]]

def search(l):
for item in l:
if type(item) is list:
search(item)
else:
print(item)

search(l)
View Code

 


 

#二分法
l = [1,2,5,7,10,31,44,47,56,99,102,130,240]


def binary_search(l,num):
print(l)
if len(l) == 1:
if l[0] == num:
print('find it')
else:
print('not exists')
return
mid_index
=len(l)//2
mid_value
=l[mid_index]
if num == mid_value:
print('find it')
return
if num > mid_value:
l
=l[mid_index:]
if num < mid_value:
l
=l[:mid_index]
binary_search(l,num)

binary_search(l,
32)

 

模块与包

模块

1 什么是模块

常见的场景:一个模块就是一个包含了python定义和声明的文件,文件名就是模块名字加上.py的后缀。

   但其实import加载的模块分为四个通用类别: 

  1 使用python编写的代码(.py文件)

  2 已被编译为共享库或DLL的C或C++扩展

  3 包好一组模块的包

  4 使用C编写并链接到python解释器的内置模块

2 为何要使用模块?

如果你退出python解释器然后重新进入,那么你之前定义的函数或者变量都将丢失,因此我们通常将程序写到文件中以便永久保存下来,需要时就通过python test.py方式去执行,此时test.py被称为脚本script。

    随着程序的发展,功能越来越多,为了方便管理,我们通常将程序分成一个个的文件,这样做程序的结构更清晰,方便管理。这时我们不仅仅可以把这些文件当做脚本去执行,还可以把他们当做模块来导入到其他的模块中,实现了功能的重复利用,

3 如何使用模块?import、from...import...

3.1 import

示例文件:spam.py,文件名spam.py,模块名spam

#spam.py
print('from the spam.py')

money=1000

def read1():
print('spam->read1->money',money)

def read2():
print('spam->read2 calling read')
read1()

def change():
global money
money=0

3.1.1 模块可以包含可执行的语句和函数的定义,这些语句的目的是初始化模块,它们只在模块名第一次遇到导入import语句时才执行(import语句是可以在程序中的任意位置使用的,且针对同一个模块很import多次,为了防止你重复导入,python的优化手段是:第一次导入后就将模块名加载到内存了,后续的import语句仅是对已经加载大内存中的模块对象增加了一次引用,不会重新执行模块内的语句),如下 

#test.py
import spam #只在第一次导入时才执行spam.py内代码,此处的显式效果是只打印一次'from the spam.py',当然其他的*代码也都被执行了,只不过没有显示效果.
import spam
import spam
import spam

'''
执行结果:
from the spam.py
'''

我们可以从sys.module中找到当前已经加载的模块,sys.module是一个字典,内部包含模块名与模块对象的映射,该字典决定了导入模块时是否需要重新导入。

3.1.2 每个模块都是一个独立的名称空间,定义在这个模块中的函数,把这个模块的名称空间当做全局名称空间,这样我们在编写自己的模块时,就不用担心我们定义在自己模块中全局变量会在被导入时,与使用者的全局变量冲突

#测试一:money与spam.money不冲突
#
test.py
import spam
money
=10
print(spam.money)

'''
执行结果:
from the spam.py
1000
'''
#测试二:read1与spam.read1不冲突
#
test.py
import spam
def read1():
print('========')
spam.read1()

'''
执行结果:
from the spam.py
spam->read1->money 1000
'''
#测试三:执行spam.change()操作的全局变量money仍然是spam中的
#
test.py
import spam
money
=1
spam.change()
print(money)

'''
执行结果:
from the spam.py
1
'''

3.1.3 总结:首次导入模块spam时会做三件事:

1.为源文件(spam模块)创建新的名称空间,在spam中定义的函数和方法若是使用到了global时访问的就是这个名称空间。

2.在新创建的命名空间中执行模块中包含的代码,见初始导入import spam

事实上函数定义也是“被执行”的语句,模块级别函数定义的执行将函数名放入模块全局名称空间表,用globals()可以查看

3.创建名字spam来引用该命名空间

这个名字和变量名没什么区别,都是‘第一类的’,且使用spam.名字的方式可以访问spam.py文件中定义的名字,spam.名字与test.py中的名字来自两个完全不同的地方。

3.1.4 为模块名起别名,相当于m1=1;m2=m1 

import spam as sm
print(sm.money)

示范用法:

import my_print

import os,sys,time

#有两中sql模块mysql和oracle,根据用户的输入,选择不同的sql功能
#
mysql.py
def sqlparse():
print('from mysql sqlparse')
#oracle.py
def sqlparse():
print('from oracle sqlparse')

#test.py
db_type=input('>>: ')
if db_type == 'mysql':
import mysql as db
elif db_type == 'oracle':
import oracle as db

db.sqlparse()

 from import

from 模块 import "名称空间"

from spam import read1,read2

这样在当前位置直接使用read1和read2就好了,执行时,仍然以spam.py文件全局名称空间

 

#测试一:导入的函数read1,执行时仍然回到spam.py中寻找全局变量money
#
test.py
from spam import read1
money
=1000
read1()
'''
执行结果:
from the spam.py
spam->read1->money 1000
'''

#测试二:导入的函数read2,执行时需要调用read1(),仍然回到spam.py中找read1()
#
test.py
from spam import read2
def read1():
print('==========')
read2()

'''
执行结果:
from the spam.py
spam->read2 calling read
spam->read1->money 1000
'''

如果当前有重名read1或者read2,那么会有覆盖效果。

#测试三:导入的函数read1,被当前位置定义的read1覆盖掉了
#
test.py
from spam import read1
def read1():
print('==========')
read1()
'''
执行结果:
from the spam.py
==========
'''

需要特别强调的一点是:python中的变量赋值不是一种存储操作,而只是一种绑定关系,如下:

from spam import money,read1
money
=100 #将当前位置的名字money绑定到了100
print(money) #打印当前的名字
read1() #读取spam.py中的名字money,仍然为1000

'''
from the spam.py
100
spam->read1->money 1000
'''

3.2.2 也支持as

1 from spam import read1 as read

 

3.2.3 也支持导入多行

1 from spam import (read1,
2 read2,
3 money)

 

3.2.4 from spam import * 把spam中所有的不是以下划线(_)开头的名字都导入到当前位置,大部分情况下我们的python程序不应该使用这种导入方式,因为*你不知道你导入什么名字,很有可能会覆盖掉你之前已经定义的名字。而且可读性极其的差,在交互式环境中导入时没有问题。

python学习点滴记录
 1 from spam import * #将模块spam中所有的名字都导入到当前名称空间
2 print(money)
3 print(read1)
4 print(read2)
5 print(change)
6
7 '''
8 执行结果:
9 from the spam.py
10 1000
11 <function read1 at 0x1012e8158>
12 <function read2 at 0x1012e81e0>
13 <function change at 0x1012e8268>
14 '''
python学习点滴记录

可以使用__all__来控制*(用来发布新版本)

在spam.py中新增一行

__all__=['money','read1'] #这样在另外一个文件中用from spam import *就这能导入列表中规定的两个名字

如果spam.py中的名字前加_,即_money,则from spam import *,则_money不能被导入

 

3.2.5 考虑到性能的原因,每个模块只被导入一次,放入字典sys.module中,如果你改变了模块的内容,你必须重启程序,python不支持重新加载或卸载之前导入的模块,

有的同学可能会想到直接从sys.module中删除一个模块不就可以卸载了吗,注意了,你删了sys.module中的模块对象仍然可能被其他程序的组件所引用,因而不会被清除。

特别的对于我们引用了这个模块中的一个类,用这个类产生了很多对象,因而这些对象都有关于这个模块的引用。

如果只是你想交互测试的一个模块,使用 importlib.reload(), e.g. import importlib; importlib.reload(modulename),这只能用于测试环境。

def func1():
print('func1')
import time,importlib
import aa

time.sleep(
20)
# importlib.reload(aa)
aa.func1()

在20秒的等待时间里,修改aa.py中func1的内容,等待test.py的结果。

打开importlib注释,重新测试

3.3 把模块当脚本执行

我们可以通过模块的全局变量__name__来查看模块名:
当做脚本运行:
__name__ 等于'__main__'

当做模块导入:
__name__=

作用:用来控制.py文件在不同的应用场景下执行不同的逻辑
if __name__ == '__main__':

def my_func():
print('my_func')

if __name__ == '__main__':
my_func()
#当作模块导入时,__name__等于python文件名;而当作脚本执行时,__name__等于'__main__',就会执行子代码

 


包:包是一种通过使用‘.模块名’来组织python模块名称空间的方式

1. 无论是import形式还是from...import形式,凡是在导入语句中(而不是在使用时)遇到带点的,都要第一时间提高警觉:这是关于包才有的导入语法

2. 包是目录级的(文件夹级),文件夹是用来组成py文件(包的本质就是一个包含__init__.py文件的目录)

3. import导入文件时,产生名称空间中的名字来源于文件,import 包,产生的名称空间的名字同样来源于文件,即包下的__init__.py,导入包本质就是在导入该文件

强调:

1. 在python3中,即使包下没有__init__.py文件,import 包仍然不会报错,而在python2中,包下一定要有该文件,否则import 包报错

2. 创建包的目的不是为了运行,而是被导入使用,记住,包只是模块的一种形式而已,包即模块

 

注意事项
1.关于包相关的导入语句也分为import和from ... import ...两种,但是无论哪种,无论在什么位置,在导入时都必须遵循一个原则:凡是在导入时带点的,点的左边都必须是一个包,否则非法。可以带有一连串的点,如item.subitem.subsubitem,但都必须遵循这个原则。

2.对于导入后,在使用时就没有这种限制了,点的左边可以是包,模块,函数,类(它们都可以用点的方式调用自己的属性)。

3.对比import item 和from item import name的应用场景:
如果我们想直接使用name那必须使用后者。


 

configparse模块

#!/usr/bin/python
#
Author:LiTianle
#
-*- coding:utf-8 -*-
import configparser
# import os
config=configparser.ConfigParser()
config.read(
'a.cfg')
##########################################################读取
#
查看所有标题
print(config.sections())#['section1', 'egon', 'Tianle']
#
查看指定标题下的key
print(config.options('Tianle'))#['e', 'sex']
#
查看指定标题下的key/values 元组形式
print(config.items('Tianle'))#[('age', '18'), ('sex', '111')]
#
查看指定标题下的key对应的value 字符串格式
print(config.get('Tianle','sex'))#male
#
查看指定标题下的key对应的value 整数格式
print(config.getint('Tianle','age'))#18
#
查看指定标题下的key对应的value 浮点格式
print(config.getfloat('Tianle','age'))#True
#
查看指定标题下的state的值 布尔格式
print(config.getboolean('Tianle','state'))#18.0

#判断是否存在某个标题
print(config.has_section('Tianle'))
#判断标题Tianle下是否有sex
print(config.has_option('Tianle','sex'))
##########################################################修改
#
删除整个标题egon
config.remove_section('egon')
#删除标题Tianle下的kv中的k;age
config.remove_option('Tianle','age')
#添加一个标题
config.add_section('Lele')
#在标题Lele下添加age=18,sex=male,state=1的配置,数字也必须是字符串格式否则会报错
config.set('Lele','age','18')
config.set(
'Lele','sex','male')
config.set(
'Lele','state','1')
#将修改写入文件
config.write(open('a.cfg','w'))