Long Way To Go 之 Python 4

装饰器(decorator)

      (“器”代表的是函数)

 

定义本质是函数

组成:高阶函数+嵌套函数 --> 装饰器

作用:用来装饰其他函数  in other words,为其他函数添加附加功能

        (eg. 假如程序已上线,如果想新增功能,但是又不能改源代码)

原则:

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

        2.不能修改被装饰的函数的调用方试

 

 

Q:  高阶\嵌套函数是个什么鬼???

高阶函数:

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

import time
def bar():
    time.sleep(3)
    print("in the bar")

def test1(func):
    start_time = time.time()
    func()   # run bar()
    stop_time = time.time()
    print("the func run time is %s"%(stop_time -start_time))   # 统计的是bar的运行时间

test1(bar) # func = bar 所以可以进行func()      传的内存地址
test1(bar())   # 把bar()的返回值传进去了        传的返回值
# BUT !调用方式变了,不是装饰器
View Code

 

      2.返回值中包含函数名(不修改函数的调用方式)

import time
def bar():
    time.sleep(3)
    print("in the bar")

def test2(func):
    print(func) # 打印func的内存地址,也就是bar的内存地址
    return func # return bar的内存地址

bar = test2(bar)
bar() # run bar
View Code

 

嵌套函数:

        在一个函数的函数体内用def去申明另一个函数

#嵌套函数
def foo():
    print("in the foo")
    def bar():    # 相当于局部变量,所以不能在外面直接调用bar(),所以要写在里面
        print("in the bar")
    bar()
foo()


# 这个不是函数的嵌套,这叫函数的调用
def test1():
    test2()
test1()
View Code

 

 

 

EXAMPLE 1

优化(@timmer)

def timer(func): #timer(test1)   把test1的内存地址传给func,func = test1
    def deco():
        start_time = time.time()
        func()      # run test1
        stop_time = time.time()
        print("the func run time is %s"%(start_time-stop_time))
    return deco   # 返回这个函数的内存地址

@timer   # 这就相当于  test1 = timer(test1)
def test1():
    time.sleep(3)
    print("in the test1")

@timer
def test2():
    time.sleep(3)
    print("in the test2")

test1()  
test2()  # 其实调用的是deco这个函数,因为timer(test1)的返回值是deco的内存地址
View Code

优化(调用时传入多个参数)

def timer(func): #timer(test1)   把test1的内存地址传给func,func = test1
    def deco(*args,**kwargs):
        start_time = time.time()
        func(*args,**kwargs)      # run test1
        stop_time = time.time()
        print("the func run time is %s"%(start_time-stop_time))
    return deco   # 返回这个函数的内存地址

@timer   # 这就相当于  test1 = timer(test1) = deco
def test1():
    time.sleep(1)
    print("in the test1")

@timer   # test2 = timer(test2)   =  deco
# test2() = deco()     所以在deco里加args
def test2(name,age):
    print("test2:",name,age,)

test1()
test2("alex",22)
View Code

 

EXAMPLE 2 !!

import time

user,passwd = "alex","abc123"
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("\033[32;1mUser has passed authenfication\033[0m")
                   res = func(*args,**kwargs) # return from home
                   print("------after authenficaiton")
                   return res
                else:
                   exit("\033[31;1mInvalid username or password\033[0m")
             elif auth_type == "ladp":
                   print("毛线ldap,不会------")
        return wrapper
    return outer_wrapper


def index(): # 首页
    print("welcome to index page")

@auth(auth_type = "local")   # 本地认证  & home = wrapper()
def home(): # 用户登录后的首页
    print("welcome to home page")
    return "from home"


@auth(auth_type = "ldap")   # 远程认证
def bbs(): # 论坛
    print("welcome to bbs page")

index()
print(home())   # return None  如果没有return func()的话
bbs()
View Code

 

ps:

函数及变量

# 没问题
def bar():
    print("in the bar")
def foo():
    print("in the foo")
    bar()

foo()



# bar定义到下面也是可以运行的
def foo():
    print("in the foo")
    bar()
def bar():
    print("in the bar")

foo()



# 会报错,因为函数和变量一样,是先定义,在调用。所以会报错
def foo():
    print("in the foo")
    bar()

foo()
def bar():
    print("in the bar")
View Code

 

 

 

生成器(generator)

 

    原理:只有在调用时才会生成相应的数据(假如一个大数据,只循坏了前5次,那么后面的数据是没有准备好的)

    作用:省内存

    特点:生成器只记住当前位置(不管前后),不能后退回返,也不能跳着走

    方法:只有一个next方法,  __next__()

 

#生成器
#数据规律的情况下
>>>print(i*2 for i in range(10))
<generator object <genexpr> at 0x0000000001E00E60>

#数据不规律
#fibonacci
>>>def fib(max):
       n,a,b = 0,0,1
       while n < max:
           yield b   #  用yield就是生成器了,用来保留函数中断的状态
           a,b = b,a+b   # 每次向右移一个数
           n = n+1
       return "----done---"

>>>print(fib(10))
<generator object fib at 0x0000000002180E60>

 

 

next方法的使用

 

f = fib(10)

# 用next方法,一个一个数字的打印
print(f.__next__())
print(f.__next__())
print(f.__next__())


# 用for循环就不会打印done
for i in f:
     print(i)


# 如果出现异常,比如只有10个数据,但是取了11次
while True:
    try:
        x = next(f)
        print("f:",x)
    except StopIteration as e:
        print("Generator return value:",e.value) # e.value = ----done----
        break

 

 

EXAMPLE

生成器并行

import time
def consumer(name):
    print("%s 准备吃包子啦!"%name)
    while True:
        baozi = yield  # yield作用是保存当前状态,然后返回

        print("包子[%s]来了,被[%s]吃了!"%(baozi,name))

#c = consumer("chenronghua")
#c.__next__()   #xxx 准备吃包子啦!
#c.__next__()  # next只是调用yield,但是不传值,所以包子出来的是none

#b1 = "韭菜馅"
#c.send(b1)   # send 不仅调用,还可以给yield传值
#c.__next__()  # 这里又传值了,所以出来的是none

def producer(name):
    c = consumer("A") # 如果只写这一行,不写next,相当于只是把函数变成生成器,而使用next之后,才能执行到baozi = yield
    c2 = consumer("B")
    c.__next__()
    c2.__next__()
    print("老子开始准备做包子啦!")
    for i in range(2):
        time.sleep(1)
        print("做了1个包,分两半!")
        c.send(i)
        c2.send(i)
producer("alex")
View Code

 

 

ps:

列表生成式

>>>print([i*2 for i in range(10)])
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
>>>
>>>[func(i) for i in range(10)] # 传函数也可以

 

 

 

迭代器(Iterator)

 

   定义:可被next()函数调用并不断返回下一个值的对象称为迭代器(Iterator)

            没有next方法,就不叫迭代器,有next方法,就是~

 

   作用:

         可以表示一个无限大的数据流(eg.全体自然数),而list是无法存储无穷尽的数

         我们不知道它的长度,只能不断用next()函数计算下一个数据,所以说,Iterator是惰性的,只有需要返回下一个数据的时候它才会计算        

   

   补充:  

          1. 可直接作用于for循环的数据类型

               a)  集合数据类型:

                     list, tuple, dict, set,str...

               b)  生成器:

                     生成器和带yield的generator function

 

           2. 可迭代对象(Iterable)

                可以直接用于for循环的对象叫“可迭代对象”

               (eg. a = [1,2,3], 是Iterable,但不是Iterator,因为没有next调用)

 

 

    总结:

          -----  !!!迭代器不一定是生成器,但生成器肯定是一个迭代器!!!

 

          -----  可作用于for循坏的对象都是Iterable

                   可作用于next()函数的对象都是Iterator对象

 

          -----  list,dict,str...虽然是Ierable,却不是Iterator

                   但可以用 iter() 函数来得到一个Iterator

 

          ----- isinstance()

 

# 判断对象是否是一个可迭代对象或者迭代器
>>>from collections import Iterable,Iterator
>>>isinstance([], Iterable)
True
>>>isinstance((), Iterable)
True
>>>isinstance("abc", Iterable)
True
>>>isinstance(100, Iterable)
False
>>>isinstance((x for x in range(10)), Iterable)
True

>>>isinstance((x for x in range(10)) ,Iterator)
True

 

Q:为什么list, dict, str ... 数据类型不是Iterator?

     因为python的Iterator对象表示的是一个数据流,Iterator对象可被next()函数调用并不断返回下一个数据(戳一下,动一下),直到没有数据取出并抛出StopIteration 错误

 

 

 

软件目录开发规范

 

不同目录间进行模块调用

自定义模块

# 目录假设:Atm-->bin, core--->atm, main

import os
import sys

#print(__file__) # 当前相对路径,看起来是绝对路径,只是因为是在pycharm里,在cmd里就是相对路径
print(os.path.abspath(__file__))  # 绝对路径
print(os.path.dirname(os.path.abspath(__file__)))  # 返回目录名,不要文件名

BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
print(BASE_DIR)

sys.path.append(BASE_DIR) # 添加坏境变量

# 导入模块
from conf import settings
from core import main

# run main里的login()函数
main.login()
C:/Users/apple/PycharmProjects/s14/day4/Atm/bin/atm.py
C:\Users\apple\PycharmProjects\s14\day4\Atm\bin\atm.py
C:\Users\apple\PycharmProjects\s14\day4\Atm\bin
C:\Users\apple\PycharmProjects\s14\day4\Atm

 

 

 



    

    

posted @ 2017-05-13 13:50  来一打烧饼  阅读(173)  评论(0编辑  收藏  举报