Day04 - Python基础4

Day04的课程要点记录
详细教程地址:Day4 - Python基础4 | python装饰器

一、装饰器

1.1 定义

装饰器本质是函数,(装饰其他函数)为其他函数添加附加功能。
原则:

  1. 不能修改被装饰的函数的源代码
  2. 不能修改被装饰的函数的调用方式

1.2 实现装饰器的知识储备:

1.2.1 函数即“变量”
1.2.2 高阶函数:
  • 把一个函数名当做实参传给另外一个函数,可以在不修改被装饰函数源代码的情况下为其添加功能。
import time
def bar():
   time.sleep(3)
   print("in the bar")

def test1(func):
   start_time = time.time()
   func()  # 就是bar(),但是改变了函数的调用方式
   stop_time = time.time()
   print("the func is running in %s second." % (stop_time - start_time))

test1(bar)      # 相当于func = bar
  • 返回值中包含函数名,不修改函数的调用方式。
import time
def bar():
   time.sleep(3)
   print("in the bar")
def test2(func):
   print(func)
   return func

# print(test2(bar))
bar = test2(bar)     # 将bar的内存地址重新给回到bar
  • 嵌套函数
    在一个函数的函数体内用def去声明一个函数

1.3 实现装饰器

高阶函数 + 嵌套函数 => 装饰器
@装饰器名 # 加在要用装饰器函数的头部

import time

def timer(func):        # timer(test1)  func = test1
   def deco():
       start_time = time.time()
       func()
       stop_time = time.time()
       print("Func is running %s second.." % (stop_time - start_time))
   return deco

@timer  # test1 = timer(test1)
def test1():
   time.sleep(3)
   print("in the test1")

test1()

被装饰函数可能出现所需参数数量不等的情况,通用写法如下:

import time

def timer(func):        # timer(test1)  func = test1
   def deco(*args, **kwargs):
       start_time = time.time()
       res = func(*args, **kwargs)
       stop_time = time.time()
       print("Func is running %s second.." % (stop_time - start_time))
       return res    # 返回被装饰函数的值
   return deco

@timer  # test1 = timer(test1)
def test1():
   time.sleep(3)
   print("in the test1")

@timer  # test2 = timer(test2)
def test2(name, age):
   time.sleep(3)
   print("in the test2", name, age)

test1()
test2("Alice", 18)

1.4 分情况使用装饰器的不同功能

其他类似,最外层加装一层

import time
user, passwd = 'alice', '1234'
def auth(auth_type):
   print("auth func:", auth_type)
   def outer_wrapper(func):
       def wrapper(*args, **kwargs):
           print("wrapper func args:", *args, **kwargs)
           if auth_type == 'local':
               username = input("Username:").strip()
               password = input("Password:").strip()
               if user == username and passwd == password:
                   print("Welcome back! %s" % username)
                   res = func(*args, **kwargs)
                   print("---------Complete authentication---------")
                   return res
               else:
                   exit("Invalid username or password.")
           elif auth_type == 'ldap':
               print("Are you kidding me?")
       return wrapper
   return outer_wrapper

def index():
   print("Welcome to index page.")

@auth(auth_type="local")
def home():
   print("Welcome to home page.")

@auth(auth_type='ldap')
def forum():
   print("Welcome to forum page.")

index()
home()
forum()

二、列表生成式,迭代器&生成器

2.1 列表生成式

生成一个含有数字1-10的列表,并且每个数字要 * 2

# 1.循环
li = []
for i in range(1, 11):
    li.append(i * 2)
print(li)

代码简洁的列表生成式

# 2.列表生成式
li2 = [i * 2 for i in range(1, 11)]
print(li2)

for循环后面还可以加上if判断

# 3.for循环后面还可以加上if判断
li3 = [i * i for i in range(1, 11) if i % 2 == 0]   # 生成1-10的平方切且可以被2整除的列表
print(li3)

2.2 generator生成器

在Python中,这种一边循环一边计算的机制,称为生成器:generator。

2.2.1 生成器特性
  1. 生成器只有在调用时才会生成相应的数据。
  2. 只记录当前的位置。
  3. 只有一个__next__()方法。(2.7为next()
2.2.1 创建生成器

创建一个generator,有很多种方法。
第一种方法很简单,只要把一个列表生成式的[]改成(),就创建了一个generator:

>>> l = [x * x for x in range(10)]
>>> l
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
>>> g = (x * x for x in range(10))
>>> g
<generator object <genexpr> at 0x000002256EAB2E60>

可以通过next()函数获得generator的下一个返回值,由于generator保存的是算法,每次调用next(g),就计算出g的下一个元素的值,直到计算到最后一个元素,没有更多的元素时,抛出StopIteration的错误。

>>> next(g)
0
>>> next(g)
1
>>> next(g)
4
>>> next(g)
9
>>> next(g)
16
>>> next(g)
25
>>> next(g)
36
>>> next(g)
49
>>> next(g)
64
>>> next(g)
81
>>> next(g)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

但不断调用next(g)实在是太变态了,正确的方法是使用for循环,因为generator也是可迭代对象:

>>> g = (x * x for x in range(10))
>>> for n in g:
...     print(n)
...
0
1
4
9
16
25
36
49
64
81

通过for循环来迭代generator,不需要关心StopIteration的错误。
generator非常强大。如果推算的算法比较复杂,用类似列表生成式的for循环无法实现的时候,还可以用函数来实现。
比如,著名的斐波拉契数列(Fibonacci),除第一个和第二个数外,任意一个数都可由前两个数相加得到:1, 1, 2, 3, 5, 8, 13, 21, 34, ...

def fib(max):
   n, a, b = 0, 0, 1
   while n < max:
       print(b)
       a, b = b, a + b
       n = n + 1
   return 'done'

可以输出斐波那契数列的前N个数:

>>> fib(10)
1
1
2
3
5
8
13
21
34
55
done

上面的函数和generator仅一步之遥。要把fib函数变成generator,只需要把print(b)改为yield b就可以了:

def fib(max):
   n, a, b = 0, 0, 1
   while n < max:
       #print(b)
       yield  b
       a, b = b, a + b
       n += 1

同样的,把函数改成generator后,可以使用__next__()来获取下一个返回值,或者使用for循环来迭代。
通过yield实现在单线程的情况下实现并发运算的效果

import time
def customer(name):
   print("%s 准备吃包子了!" % name)
   while True:
       baozi = yield
       print("包子[%s]来了,被[%s]吃了!" % (baozi, name))

def producer(name):
   c = customer('A')
   c2 = customer('B')
   c.__next__()
   c2.__next__()
   print("%s要开始做包子了啊!" % name)
   for i in range(10):
       time.sleep(1)
       print("第%d批包子做好了!" % (i + 1))
       c.send(i + 1)
       c2.send(i + 1)

producer("Will")

三、迭代器

3.1 Iterable可迭代对象

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

  1. 集合数据类型,如list、tuple、dict、set、str等;
  2. generator:包括生成器和带yield的generator function。
    这些可以直接作用于for循环的对象统称为可迭代对象:Iterable
    可以使用isinstance()判断一个对象是否是Iterable对象:
>>> from collections import Iterable
>>> isinstance([], Iterable)
True
>>> isinstance({}, Iterable)
True
>>> isinstance('abc', Iterable)
True
>>> isinstance((x for x in range(10)), Iterable)
True
>>> isinstance(100, Iterable)
False

3.2 Iterator迭代器

生成器不但可以作用于for循环,还可以被next()函数不断调用并返回下一个值,直到最后抛出StopIteration错误表示无法继续返回下一个值了。
可以被next()函数调用并不断返回下一个值的对象称为迭代器:Iterator
可以使用isinstance()判断一个对象是否是Iterator对象:

>>> from collections import Iterator
>>> isinstance((x for x in range(10)), Iterator)
True
>>> isinstance([], Iterator)
False
>>> isinstance({}, Iterator)
False
>>> isinstance('abc', Iterator)
False

3.3 将Iterable可迭代对象变为Iterator迭代器

生成器都是Iterator对象,但list、dict、str虽然是Iterable,却不是Iterator。
把list、dict、str等Iterable变成Iterator可以使用iter()函数:

>>> isinstance(iter([]), Iterator)
True
>>> isinstance(iter('abc'), Iterator)
True

3.4 为什么list、dict、str等数据类型不是Iterator

这是因为Python的Iterator对象表示的是一个数据流,Iterator对象可以被next()函数调用并不断返回下一个数据,直到没有数据时抛出StopIteration错误。可以把这个数据流看做是一个有序序列,但我们却不能提前知道序列的长度,只能不断通过next()函数实现按需计算下一个数据,所以Iterator的计算是惰性的,只有在需要返回下一个数据时它才会计算。
Iterator甚至可以表示一个无限大的数据流,例如全体自然数。而使用list是永远不可能存储全体自然数的。

3.5 小结

  1. 凡是可作用于for循环的对象都是Iterable类型;
  2. 凡是可作用于next()函数的对象都是Iterator类型,它们表示一个惰性计算的序列;
  3. 集合数据类型如list、dict、str等是Iterable但不是Iterator,不过可以通过Iter()函数获得一个Iterator对象。
  4. Python的for循环本质上就是通过不断调用next()函数实现的,例如:
for x in [1, 2, 3, 4, 5]:
    pass

五、匿名函数

匿名函数就是不需要显式的指定函数

#这段代码
def calc(n):
    return n**n
print(calc(10))
 
#换成匿名函数
calc = lambda n:n**n
print(calc(10))

匿名函数主要是和其它函数搭配使用的

>>> res = map(lambda x:x**2, [1, 5, 7, 4, 8])
>>> for i in res:print(i)
... 
1
25
49
16
64

四、内置函数

内置参数详解

Built-in Functions

函数名 函数功能 函数名 函数功能
abs() 取绝对值 all() 一个可迭代对象中的所有元素均为True,则返回True。为空时返回False
any() 一个可迭代对象中的任一元素为True,则返回True。为空时返回False ascii() 将一个对象变为可打印的字符串形式
bin() 将一个整数转换为一个二进制字符串 bool() 根据返回布尔值,True or False
bytearray() 返回bytes数组,该数组可以修改 bytes() 返回为bytes对象
callable() 判断是否可调用,True or False compile() 编译对象
chr() 返回数字所对应的Unicode字符 ord() 返回字符所对应的Unicode编码
dir() 查询参数所属类型的可用方法 divmod() 输入2个参数,返回他们的商和余数
enumerate 枚举 eval() 将字符串str当成有效的表达式来求值并返回计算结果
exec() 执行储存在字符串或文件中的Python语句 filter() 按照条件过滤
map() 按照条件生成新的列表 frozenset() 不可修改的集合
globals() 以字典的形式返回整个程序文件内的变量情况 hash() 返回该对象的hash值
hax() 将数字转为16进制 local() 返回局部变量
max() 返回参数中的最大值 min() 返回参数中的最小值
oct() 将数字转为8进制 pow() 求幂x**y
round() 保留小数后n位 sorted() 转为列表后排序
vars() 返回对象所有属性名 zip() 将两组列表以元组形式拼在一起

五、软件目录结构规范

5.1 为什么要设计好目录结构?

"项目目录结构"其实也是属于"可读性和可维护性"的范畴,我们设计一个层次清晰的目录结构,就是为了达到以下两点:

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

5.2 目录组织方式

关于如何组织一个较好的Python工程目录结构,已经有一些得到了共识的目录结构。在Stackoverflow的这个问题上,能看到大家对Python目录结构的讨论。
假设你的项目名为foo, 我比较建议的最方便快捷目录结构这样就足够了:

Foo/
|-- bin/
|   |-- foo
|
|-- foo/
|   |-- tests/
|   |   |-- __init__.py
|   |   |-- test_main.py
|   |
|   |-- __init__.py
|   |-- main.py
|
|-- docs/
|   |-- conf.py
|   |-- abc.rst
|
|-- setup.py
|-- requirements.txt
|-- README

简要解释一下:

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

除此之外,有一些方案给出了更加多的内容。比如LICENSE.txt,ChangeLog.txt文件等,我没有列在这里,因为这些东西主要是项目开源的时候需要用到。如果你想写一个开源软件,目录该如何组织,可以参考这篇文章

关于README的内容
这个我觉得是每个项目都应该有的一个文件,目的是能简要描述该项目的信息,让读者快速了解这个项目。
它需要说明以下几个事项:

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

可以参考Redis源码中Readme的写法,这里面简洁但是清晰的描述了Redis功能和源码结构。

不同目录间进行模块调用

调用目录


作业:员工信息表程序 - 详细描述参考

  1. 实现增删改查操作:
  2. 可进行模糊查询,语法至少支持下面3种:
    select name,age from staff_table where age > 22
    select * from staff_table where dept = "IT"
    select * from staff_table where enroll_date like "2013"
  3. 查到的信息,打印后,最后面还要显示查到的条数
  4. 可创建新员工纪录,以phone做唯一键,staff_id需自增
  5. 可删除指定员工信息纪录,输入员工id,即可删除
  6. 可修改员工信息,语法如下:
    UPDATE staff_table SET dept = "Market" WHERE where dept = "IT"
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,372评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,368评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,415评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,157评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,171评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,125评论 1 297
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,028评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,887评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,310评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,533评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,690评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,411评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,004评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,659评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,812评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,693评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,577评论 2 353

推荐阅读更多精彩内容

  • 1.1==,is的使用 ·is是比较两个引用是否指向了同一个对象(引用比较)。 ·==是比较两个对象是否相等。 1...
    TENG书阅读 729评论 0 0
  • 教程总纲:http://www.runoob.com/python/python-tutorial.html 进阶...
    健康哥哥阅读 2,024评论 1 3
  • python学习笔记 声明:学习笔记主要是根据廖雪峰官方网站python学习学习的,另外根据自己平时的积累进行修正...
    renyangfar阅读 3,040评论 0 10
  • 切片 取一个list(列表)或tuple(元组)的部分元素是非常常见的操作Python提供了切片(Slice)操作...
    upupSue阅读 543评论 0 1
  • easy_install和pip都是用来下载安装Python一个公共资源库PyPI的相关资源包的 首先安装easy...
    2010jing阅读 835评论 2 0