前言:

继续前进

基础回顾:

1、集合

集合有2个重要作用:关系测试(并集,差集,交集)和去重。

2、文件编码

2.7上默认文件编码是ASCII码,因为不支持中文,就出了GB2312,在2.7上要支持中文就必须申明文件编码以UTF-8的格式,UTF-8与GB2312的关系?

UTF-8是Unicode的扩展集合,Unicode包括全国地区的编码,中国很多开始程序还是以GBK的格式,GBK向下兼容GB2312,Windows默认编码是GBK。

Unicode为何要做出来?为了节省空间,存英文中文都是2个字节,本来我用ASCII码存英文只用1个字节,但是现在用你2个,所以出了UTF-8 ,存英文是1个字节,中文统一3个字节。

假如1个文件是GBK编码的,另外一个是UTF-8,如果它要读这个文件,就要进行一个转换,但是他们之间不能直接转换,这个时候就涉及到了转码的问题。所以GBK转换成UTF-8,语法是先decode 成Unicode,然后在encode成utf-8,见下图:

在3.0中,默认编码是Unicode,在2.7中要打印中文就得申明字符编码 # -*- coding:utf-8 -*-

在3.0可以不写,默认文件编码就是Unicode,那么现在文件编码就是Unicode,因为Unicode本来也支持中文,按2个字节存放,不需要转换成utf-8,要想变成utf-8也得encode一下,如下所示:

a= '我是'.encode("utf-8")

。当然也可以申明字符编码 # -*- coding:utf-8 -*- ,那么现在的文件编码就是utf-8了。

3、函数

格式如下:

def  func_name():pass

位置参数,比如 arg1 和 arg2

def  func_name(arg1,arg2):passfunc_name(5,3)

5对应的是arg1  3对应的是arg2

关键参数,可以指定参数名,比如:

def  func_name(arg1,arg2,arg3):passfunc_name(1,2,arg3=5)

注意,关键参数不能写在位置参数前面。

多个参数,就用到了*args,比如:

def   func_name(arg1,arg2,*args):passfunc_name(4,5,6,7,8)
那么打印出来效果
4,5,(6,7,8)

把后面非固定参数写成了元祖

**kwargs ,打印出来是一个字典,例如

def func_name(arg1,arg2,arg3,*args,**kwargs):passfunc_name(3,4,55,666,77,name=xiedi)打印出来的结果
3,4,55,(666,77),{'name':'xiedi'}

4、局部变量和全局变量

总的来说,局部变量只对函数内生效,对函数外不起作用。

它涉及到一个作用域的问题,只是在函数里生效的,函数执行完毕,变量就没了,作用域只允许在函数里改东西。

找变量的顺序,先从内到外找变量。

如果非得改变它的作用域,就加一个global,但是不建议这么做,例如

age = 22
def change_age():global ageage = 24

5、返回值

返回值是因为我想得到函数的执行结果,它还代表着程序的结束

6、递归

递归相当于自己调自己,有几个条件:

1、要有一个明确的结束条件。

因为递归相当于一层进入一层。

2、问题规模每递归一次都应该比上一次的问题规模有所减少。

3、效率低

7、高阶函数

把一个函数当做另一个函数的参数传进去,返回的时候要用到这个函数。

函数式编程是不需要变量的,纯粹是一个映射关系,函数式编程是没有副作用的,就是传进去的数据是确定的,得出来的结果也是固定的。

8、文件操作

打开模式:

f = open

r,w,a

r是读,w是写,它会覆盖,a是追加,r+是读写模式,写到后面,追加的模式。

w+ 是写读,以写的模式打开文件,如果文件存在,直接覆盖。

a+追加写读

rb二进制模式打开,全部是字节格式

获得文件句柄

操作:

f.

关闭:

f.close

接下来就是重点了,先来个装饰器。顾名思义,装饰一下。

一、装饰器

从字面意思来看,器代表函数的意思,可以说,装饰器本身就是函数,都是用def语法来定义的。

装饰器:

定义:本质是函数,(装饰其他函数)

为其他函数添加附加功能。

①先来看个简单的,在没学函数之前,我想给定义的函数打个日志,写法如下:

def  test1():passprint('logging')def test2():passprint('logging')
#调用
test1()
test2()

②接下来学了函数,我就把打日志定义成一个函数

# -*- coding: utf-8 -*-
#Author: Leon xiedef logger():print('logging')def  test1():passlogger()def test2():passlogger()
#调用
test1()
test2()

假设我写的函数已经上线运行了,某一天,我有个需求,在这个里面新增一个功能,那怎么去做这个事?

最简单的就是:挨个找到100个函数,加上去。但是问题是程序已经运行了,我刚才操作是修改我程序的源代码,会有风险发生。

所以说,我要新增一个功能,不能够修改函数的源代码,函数一旦写好了,原则上不能动源代码了。

所以就有了下面的原则:

原则:

1、不能修改被装饰函数的源代码。

2、不能修改被装饰的函数的调用方式。

装饰器对于被装饰函数是完全透明的。他没有动我的源代码,我该怎么调用运行就怎么运行。

举例子:

定义1个函数

#!/usr/bin/env python
# -*- coding: utf-8 -*-
#Author: Leon xieimport timedef test1():time.sleep(3)print('in the test1')test1()这个函数实现的就是  睡3秒然后打印

接下来写个装饰器:

用的时候只要在函数前面加一个“@函数名”, 即可

先睡3s然后打印,随后统计了一个test1函数的运行时间。

第一:装饰器本质就是一个函数

第二:装饰器不修改被装饰函数的源代码和调用方式

第三:对于函数 test1来说,装饰器完全不存在。

实现这个装饰器的功能需要哪些知识呢?

1、函数即变量

2、高阶函数

3、嵌套函数

最终:

高阶函数+嵌套函数===>装饰器

我们来复习一下变量:

变量是存在内存当中,比如我x=1,那么它是如何存在变量中呢?如下图:

其实我要说的就是函数即变量。

变量调用加上变量名直接调用。

函数调用呢就是函数加个小括号。    test()

python解释器中有一个概念叫做引用计数。

比如x=1 ,y=x,那么就是2次计数。

x和y相当于房间的门牌号,如果没有门牌号了,那么内存里的1就会被清空。

匿名函数:

有的函数是不定义名字的。

例如:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
#Author: Leon xie
#为了后面调用,我起了一个变量名,这个函数没有名字
calc = lambda x:x*3
print(calc(3))输出结果9

匿名函数没有def起函数名。

小结:

函数就是一个变量,定义一个函数,就是把函数体付给了这个函数名。

变量特性是:内存回收。

既然说函数即变量那么下面这个函数如何存放呢?

def foo():

  print('in the foo')

bar()

foo()

这个函数就回报错,如下图所示:

变量是先定义,后引用,函数也是一样。

看下面这个例子:可以正常调用,只要在调用之前存在就可以调用

def foo():

  print('in the foo')

bar()

def bar():

  print('in the bar')

foo()

高阶函数:(满足下面2个条件)

a:把一个函数名当做实参传给另外一个函数(在不修改被装饰函数源代码的情况下为其添加功能)

b:返回值中包含函数名

按照第一条原则写一个

#!/usr/bin/env python
# -*- coding: utf-8 -*-
#Author: Leon xiedef bar():print('in the bar')def test1(func):print(func)test1(bar)

输出结果

<function bar at 0x0000000000A69268>一段内存地址

上面相当于

func= bar 是一个门牌地址

func()是可以运行的,所以可以写成这样 类似于x=1   y=x

那么就有了下面的函数,附加一个计数的功能。

#!/usr/bin/env python
# -*- coding: utf-8 -*-
#Author: Leon xieimport  time
def bar():time.sleep(3)print('in the bar')def test1(func):start_time =time.time()#运行一下funcfunc()stop_time =time.time()#传进来的运行时间不是test1print("the func run time is %s" %(stop_time-start_time))test1(bar)

输出结果

in the bar
the func run time is 3.0002999305725098

这里在没有修改源代码的基础上新增了一个计数的功能。不过我们知道装饰器还有一个条件就是不改变调用方式。所以我们接着往下看

嵌套函数举例:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
#Author: Leon xiedef foo():print('in the foo')def bar():print('in the bar')bar()
foo()输出结果

in the foo
in the bar

最后装饰器效果:

#写个装饰器统计运行的时间import timedef timer(func):  #timer(test1)  test1 的内存地址给了funcdef deco(*args,**kwargs):start_time=time.time()func(*args,**kwargs)stop_time= time.time()print('the func run time is %s' %(stop_time-start_time))return deco     #返回了deco的内存地址#嵌套函数写成下面的形式
#def timer():#   def deco():#     pass@timer   #test1= timer(test1)
def  test1():time.sleep(1)print('in the test1')@timer   #test2= timer(test2)
def test2(name,age):time.sleep(1)print("test2:",name,age)test1()
test2("xiedi",22)

输出结果

in the test1
the func run time is 1.0
test2: xiedi 22
the func run time is 1.0

升级

#!/usr/bin/env python
# -*- coding: utf-8 -*-
#Author: Leon xie#需求:公司有网站,有很多页面,模拟1个页面1个函数,在之前情况谁都可以登录没有任何验证
#100个页面有20个登录以后才能看到,就说给20个加入验证功能。import time
user,passwd = 'xiedi','123'
def auth(func):def wrapper(*args,**kwargs):username = input("Username:").strip()password = input("Password:").strip()if user == username and passwd == password:print("\033[32;1mUser has passed authentication\033[0m")res = func(*args,**kwargs)print("--afterauthenticaion")return reselse:exit("\033[31;1mInvalid username or password\033[0m")return wrapperdef index():print("welcome to index page")@auth
def home():print("welcome to hoem page")return "from home"@auth
def bbs():print("welcome to bbs page")index()
print(home())
bbs()

View Code

输出结果

welcome to index page
Username:xiedi
Password:123
User has passed authentication
welcome to hoem page
--afterauthenticaion
from home
Username:

升级,加入新的判断,登录判断

#!/usr/bin/env python
# -*- coding: utf-8 -*-
#Author: Leon xie#需求:公司有网站,有很多页面,模拟1个页面1个函数,在之前情况谁都可以登录没有任何验证
#100个页面有20个登录以后才能看到,就说给20个加入验证功能。#可不可以让home认证的时候使用本地认证,bbs用远程认证import time
user,passwd = 'xiedi','123'
def auth(auth_type):print("auth func:",auth_type)def outer_wrapper(func):def wrapper(*args,**kwargs):print("wrapper func args:",args,**kwargs)username = input("Username:").strip()password = input("Password:").strip()if user == username and passwd == password:print("\033[32;1mUser has passed authentication\033[0m")res = func(*args,**kwargs)print("--afterauthenticaion")return reselse:exit("\033[31;1mInvalid username or password\033[0m")return wrapperreturn outer_wrapperdef index():print("welcome to index page")@auth(auth_type = "local")
def home():print("welcome to hoem page")return "from home"@auth(auth_type = "ldap")
def bbs():print("welcome to bbs page")index()
home()
bbs()

输出结果

auth func: local
auth func: ldap
welcome to index page
wrapper func args: ()
Username:xiedi
Password:123
User has passed authentication
welcome to hoem page
--afterauthenticaion
wrapper func args: ()
Username:xiedi
Password:123
User has passed authentication
welcome to bbs page
--afterauthenticaionProcess finished with exit code 0

二、迭代器和生成器

列表生成式:

我们到列表的定义,比如a=[1,2,3],我们还可以这么写[i*2 for i in range(10)]

就是i在range(10)做一个for循环,然后乘以2得到一个列表。这个就叫做列表生成式。主要作用是使代码更简洁。

还可以在前面执行一个函数,如下图:

生成器:

通过列表生成式,我们可以直接创建一个列表,但是,收到内存限制,列表容量肯定是有限的。

比如我创建100W元素的列表,我只用前面几个,后面都不用,是不是浪费?

所以,如果列表元素可以按照某种算法推算出来,那我们就不必创建完整的list,从而节省大量的空间,在Python中,这种一边循环一边计算的机制,称为生成器:generator。

怎么去节省内存呢?循环列表是1个1个循环,列表从头循环到尾的时候,我循环10次,循环到第5次的时候,后面的5个数据是已经准备好的。剩下的就很占用空间,那么我能不能搞个机制出来,我循环到第4次的时候,第4次的数据才刚生成。剩下的我不调用就没有

这样我就不需要提前把数据准备好了,省了空间了。

那么数据是怎么生成呢?有规律的做法

这样就是没循环一次乘以2了。你访问它,它才会生成。

生成器,只有在调用时才会生成相应的数据。

生成器只记住当前这个位置,它也不知道前面,也不知道后面,前面用完了对它来讲没了,它只保存一个值。

1、只记录当前位置

2、只有一个_next_()方法。

(i*i  for  i in range(10))这个语句高了一个生成器。

如果后面生成数据没有规律那怎么办?

再次,创建一个生成器:

用函数来做一个生成器。

斐波拉契数列,除第一个和第二个数外,任意一个数都可以由前面2个数相加得到

1,1,2,3,5,8,13,21,34。。。。

规则就是如此。

他是有一定规律就可以推导出来。

#!/usr/bin/env python
# -*- coding: utf-8 -*-
#Author: Leon xiedef fib(max):n, a, b = 0,0,1while  n <max:print(b)a,b =b ,a+bn=n+1return 'done'fib(10)

结果

1
1
2
3
5
8
13
21
34
55

分析:

a,b=1,2

a=1

b=2

t=(b,a+b)

所以这个时候

a=2 b=3了

把上面函数改成生成器,1步即可

变成了一个生成器。

这样做的好处在哪呢?

之前,我们调用函数,如果函数在执行时候需要花费10分钟,那么我接下来的操作就要在10分钟后进行。程序就卡在这了

现在这个呢?现在函数变成生成器之后,我直接调用一下next,它就在里面循环一次,停在这了,程序就跑到外面了,我可以干点别的事在回去。例如:

这样就把函数做成了一个生成器。

接下来有个问题,就是如果我取得数大于10,用next 方法取不到就会报一个异常。如何解决呢?

就是要抓住这个异常: try一下

g = fib(6)while True:try:x = next(g)print('g:',x)except StopIteration as e:print('Generator return value:',e.value)break

yield是保存了函数的中断状态,返回当前状态的值,函数停在这了,一会还可以回来。

工作中如何使用呢?

我们可以通过yield来实现单线程的情况下实现并发运算的效果

#!/usr/bin/env python
# -*- coding: utf-8 -*-
#Author: Leon xieimport time#典型的生产者消费者模型
def consumer(name):print("%s 准备吃包子啦!!" %name)while True:baozi = yieldprint("包子[%s]来了,被[%s]吃了" %(baozi,name))c = consumer("xiedi")
c.__next__()b1 = "韭菜馅"
c.send(b1)
#c.__next__()def producer(name):c = consumer('A')c2 = consumer('B')c.__next__()c2.__next__()print("老子开始准备做包子了!")for i in range(10):time.sleep(1)print("做了2个包子")c.send(i)
producer("dd")

输出结果

xiedi 准备吃包子啦!!
包子[韭菜馅]来了,被[xiedi]吃了
A 准备吃包子啦!!
B 准备吃包子啦!!
老子开始准备做包子了!
做了2个包子
包子[0]来了,被[A]吃了
做了2个包子
包子[1]来了,被[A]吃了
做了2个包子
包子[2]来了,被[A]吃了
做了2个包子
包子[3]来了,被[A]吃了
做了2个包子
包子[4]来了,被[A]吃了
做了2个包子
包子[5]来了,被[A]吃了
做了2个包子
包子[6]来了,被[A]吃了
做了2个包子
包子[7]来了,被[A]吃了
做了2个包子
包子[8]来了,被[A]吃了
做了2个包子
包子[9]来了,被[A]吃了

 迭代器:

可直接作用于for循环的数据类型有以下几种:

一类是集合数据类型,如list,tuple ,dict ,set ,str等。

一类是generator,包括生成器和带yield的 generator function。

可以使用isinstance()判断一个对象是否是Iterable对象。

可以被next()函数调用并不断返回下一个值得对象统称为迭代器。

可以直接作用于for循环的对象统称为可迭代对象:Iterable 。

三、软件目录结构规范

目录结构目的

  1. 可读性高: 不熟悉这个项目的代码的人,一眼就能看懂目录结构,知道程序启动脚本是哪个,测试目录在哪儿,配置文件在哪儿等等。从而非常快速的了解这个项目。
  2. 可维护性高: 定义好组织规则后,维护者就能很明确地知道,新增的哪个文件和代码应该放在什么目录之下。这个好处是,随着时间的推移,代码/配置的规模增加,项目结构不会混乱,仍然能够组织良好。

假设你的项目名为foo, 我比较建议的最方便快捷目录结构这样就足够了:

Foo/    项目名
|-- bin/   可执行放的目录
|   |-- foo   启动foo调用main
|
|-- foo/    主程序目录
|   |-- tests/   测试的,程序的主逻辑,测试代码
|   |   |-- __init__.py
|   |   |-- test_main.py
|   |
|   |-- __init__.py   必须有,这是一个空文件
|   |-- main.py    程序主入口,启动foo去调用main
|
|-- docs/    文档
|   |-- conf.py
|   |-- abc.rst
|
|-- setup.py    安装部署的脚步
|-- requirements.txt    依赖关系,比如依赖安装mysql
|-- README  ---conf     配置文件目录

简要解释一下:

  1. bin/: 存放项目的一些可执行文件,当然你可以起名script/之类的也行。
  2. foo/: 存放项目的所有源代码。(1) 源代码中的所有模块、包都应该放在此目录。不要置于顶层目录。(2) 其子目录tests/存放单元测试代码; (3) 程序的入口最好命名为main.py
  3. docs/: 存放一些文档。
  4. setup.py: 安装、部署、打包的脚本。
  5. requirements.txt: 存放软件依赖的外部Python包列表。
  6. README: 项目说明文件。
  7. conf:配置文件目录

关于README的内容

这个我觉得是每个项目都应该有的一个文件,目的是能简要描述该项目的信息,让读者快速了解这个项目。

它需要说明以下几个事项:

  1. 软件定位,软件的基本功能。
  2. 运行代码的方法: 安装环境、启动命令等。
  3. 简要的使用说明。
  4. 代码目录结构说明,更详细点可以说明软件的基本原理。
  5. 常见问题说明。

我觉得有以上几点是比较好的一个README。在软件开发初期,由于开发过程中以上内容可能不明确或者发生变化,并不是一定要在一开始就将所有信息都补全。但是在项目完结的时候,是需要撰写这样的一个文档的。

可参考:https://github.com/antirez/redis#what-is-redi

转载于:https://www.cnblogs.com/jixuege-1/p/5779464.html

一个初学者的辛酸路程-装饰器-5相关推荐

  1. 初学者python笔记(装饰器后篇:登陆验证)

    装饰器有非常强大的功能,可以不修改函数源代码和调用方式,就给函数加上了对应想要的功能,简直就是 修饰函数的利器. 上一篇文章:初学者python笔记(装饰器.高阶函数.闭包)已经非常详细的剖析了装饰器 ...

  2. 初学者python笔记(装饰器、高阶函数、闭包)

    一个函数被定义完成后,甚至程序发布后,后期可能需要添加某些功能,但是我们不可能每次都去修改原函数的代码,这时候装饰器就可以上场了,本篇文章将会用一个个可实现的代码,由浅入深.循序渐进得阐述装饰器的强大 ...

  3. [转载]一个任务超时退出的装饰器,用起来真方便

    来源:Python技巧 | 一个任务超时退出的装饰器,用起来真方便~ 本篇装饰器的写法,倒是蛮值得学习的,贴一下. 文章目录 1 任务超时退出 2 日志记录 3 缓存 4 约束某个函数的可执行次数 1 ...

  4. python四大高阶函数_详谈Python高阶函数与函数装饰器(推荐)

    一.上节回顾 Python2与Python3字符编码问题,不管你是初学者还是已经对Python的项目了如指掌了,都会犯一些编码上面的错误.我在这里简单归纳Python3和Python2各自的区别. 首 ...

  5. Python 函数装饰器

    装饰器(Decorators)是 Python 的一个重要部分.简单地说:他们是修改其他函数的功能的函数.他们有助于让我们的代码更简短,也更Pythonic(Python范儿).大多数初学者不知道在哪 ...

  6. python return用法_初学Python要了解什么 装饰器知识汇总有哪些

    初学Python要了解什么?装饰器知识汇总有哪些?在Python学习过程中,有多种方法对函数和类进行加工,相对于其它方式,装饰器语法简单,代码可读性高.因此,装饰器在Python项目中有广泛的应用,比 ...

  7. 一文读懂 Python 装饰器

    Python 是一种对新手很友好的语言.但是,它也有很多较难掌握的高级功能,比如装饰器(decorator).很多初学者一直不理解装饰器及其工作原理,在这篇文章中,我们将介绍装饰器的来龙去脉. 在 P ...

  8. CHAR.VI 函数装饰器和闭包

    CHAR.VI 函数装饰器和闭包 函数装饰器用于在源码中"标记"函数,以某种方式增强函数的行为.这是一项强大的功能,但是若想掌握,必须理解闭包. nonlocal 是新近出现的保留 ...

  9. 装饰器模式在MyBatis以及Spring源码中的应用

    结构型模式                 ----顺口溜:适装桥组享代外 目录 1.装饰器模式 1.1 装饰器模式UML图 1.2 日常生活中看装饰器模式 1.3 使用场景 1.4 Java代码实现 ...

最新文章

  1. 在 BT5 下对 Red Hat Enterprise Linux 5.4 的一次***测试
  2. Altium Designer09解决局域网冲突问题
  3. nginx 正向代理配置
  4. 使用Caffeine和Spring Boot的多个缓存配置
  5. python 实例 cadu_【示例详解】AutoCAD处理控件Aspose.CAD 8月新更!支持加载大型DWG文件...
  6. cobol和java区别,COBOL语法和文法(1)
  7. iphonexr电池容量_iPhone12mini电池容量多少毫安能用多久 iPhone12mini适合打游戏王者吗...
  8. Layui中的table中toolbar自定义过程
  9. 8uftp cuteftp,8uftp cuteftp之间的差别
  10. 电压基准和稳压电源-BUCK\BOOST原理讲解
  11. 超好用的两款作图工具,用起来~~~
  12. 什么是二进制8421码?
  13. 【小白】如何写好自己的一篇CSDN博客(美化1)
  14. python中怎么打印出表格_Python 表格打印
  15. “去中心化”和“分布式”的区别
  16. hbuilder运行uniapp,微信开发者工具打开但没有运行项目
  17. 相信技术的力量 - RSAC 2020 (2)
  18. 如何将ios键盘返回由英文设置成中文
  19. 小程序组件的初始化方法attached
  20. 大数据告诉你,北上广深哪里最容易吃到你的家乡味儿?

热门文章

  1. java dcompiler 破解,小编教你解决win7系统玩鬼泣5提示计算机中DCOMPILeR_43.dll的恢复方案...
  2. vivo分屏_在家学习效率低?vivo手机“分屏多任务”,教你提升学习效率
  3. pdf文件生成及条形码生成
  4. 01_美国医疗保健分析的入门介绍
  5. 简单奇妙,CSS开发者的8个有趣CSS
  6. python的个税计算代码
  7. vue中在线引入iconfont字体图标
  8. jgs--多线程和synchronized
  9. 第32篇-某加速网站登录中的RSA算法【2022-06-17】
  10. vs2019特殊符号输出出错