Home  >  Article  >  Backend Development  >  Sample code sharing that allows you to have an in-depth understanding of python decorators

Sample code sharing that allows you to have an in-depth understanding of python decorators

黄舟
黄舟Original
2017-06-18 11:33:371343browse

The decorator in Python is a hurdle for you to enter Python. It is there whether you cross it or not. The concept of decorators in Python often confuses people, so today I will analyze the decorators in Python

1. Scope

In python, scope is divided into two types: global scope and local scope.

The global scope is the variable and function name defined at the file level. The local scope is inside the defining function.

Regarding scope, I need to understand two points: a. Locally defined variables cannot be accessed globally b. Globally defined variables can be accessed locally, but globally defined variables cannot be modified (of course there are methods Can be modified)

Let’s take a look at the following example:


x = 1
def funx():
  x = 10
  print(x) # 打印出10

funx()
print(x) # 打印出1

If there is no defined variable x locally, then the function will Start searching for The file is referenced anywhere, but the modification can only be performed globally; if the required variable is not found locally, it will be searched externally, and an error will be reported if it is not found.


2. Advanced functions

We know that the function name actually points to the address of a memory space. Since it is an address, we can use this feature.

aThe function name can be used as a value

x = 1
def funx():
  print(x) # 打印出1

funx()
print(x) # 打印出1

x = 1
def funx():
  def func1():
    print(x) # 打印出1
  func1()

funx()
print(x) # 打印出1

b.The function name can be used as the return value


def delete(ps):
  import os
  filename = ps[-1]
  delelemetns = ps[1]
  with open(filename, encoding='utf-8') as f_read,\
    open('tmp.txt', 'w', encoding='utf-8') as f_write:
    for line in iter(f_read.readline, ''):
      if line != '\n': # 处理非空行
        if delelemetns in line:
          line = line.replace(delelemetns,'')
        f_write.write(line)
  os.remove(filename)
  os.rename('tmp.txt',filename)

def add(ps):
  filename = ps[-1]
  addelemetns = ps[1]
  with open(filename, 'a', encoding='utf-8') as fp:
    fp.write("\n", addelemetns)

def modify(ps):
  import os
  filename = ps[-1]
  modify_elemetns = ps[1]
  with open(filename, encoding='utf-8') as f_read, \
      open('tmp.txt', 'w', encoding='utf-8') as f_write:
    for line in iter(f_read.readline, ''):
      if line != '\n': # 处理非空行
        if modify_elemetns in line:
          line = line.replace(modify_elemetns, '')
        f_write.write(line)
  os.remove(filename)
  os.rename('tmp.txt', filename)


def search(cmd):
  filename = cmd[-1]
  pattern = cmd[1]
  with open(filename, 'r', encoding="utf-8") as f:
    for line in f:
      if pattern in line:
        print(line, end="")
    else:
      print("没有找到")

dic_func ={'delete': delete, 'add': add, 'modify': modify, 'search': search}

while True:
  inp = input("请输入您要进行的操作:").strip()
  if not inp:
    continue
  cmd_1 = inp.split()
  cmd = cmd_1[0]
  if cmd in dic_func:
    dic_func[cmd](cmd_1)
  else:
    print("Error")

c.. The function name can be used as a parameter


def outer():
  def inner():
    pass
  return inner

s = outer()
print(s)

######输出结果为#######
<function outer.<locals>.inner at 0x000000D22D8AB8C8>

Therefore, if one of the above two conditions is met, it can be called an advanced function.


3. Closure function

Closure function must meet two conditions: 1. Function defined inside the function 2. Contains a reference to the external scope instead of the global scope

The following illustrates the closure function through some examples:

Example 1: The following only defines a function inside the function, but it is not a closure function.

def index():
  print("index func")

def outer(index):
  s = index
  s()
  
outer(index)

######输出结果#########

index func

Example 2: The following defines a function inside the function and also references an external variable x. Is this a closure function? Answer: No


##

def outer():
  def inner():
    print("inner func excuted")
  inner() # 调用执行inner()函数
  print("outer func excuted")
outer() # 调用执行outer函数

####输出结果为##########
inner func excuted
outer func excuted

Looking back at the definition of closure function, does it satisfy both of them? If you are smart, you must find that the second one is not satisfied. Yes, the variable x here is a global variable, not an external effect. variables in the domain. Let’s take a look at the following example:


x = 1
def outer():
  def inner():
    print("x=%s" %x) # 引用了一个非inner函数内部的变量
    print("inner func excuted")
  inner() # 执行inner函数
  print("outer func excuted")

outer()
#####输出结果########
x=1
inner func excuted
outer func excuted

Obviously, the above example satisfies the conditions of the closure function. Now, you should know that as a closure function, it must meet the above two conditions, one of which is indispensable. However, under normal circumstances, we will return a value to the closure function. Let’s not talk about why here. In the following content, you will see the use of this return value.


def outer():
  x = 1
  def inner():
    print("x=%s" %x)
    print("inner func excuted")
  inner()
  print("outer func excuted")

outer()

#####输出结果#########
x=1
inner func excuted
outer func excuted

Now let’s define the closure function abstractly. It is an entity composed of a function and its associated reference environment. When implementing deep constraints, you need to create something that explicitly represents the reference environment and bundle it with the relevant subroutine, so that the bundle becomes a closure. In the above example, we can find that the closure function must contain its own function and an external variable to truly be called a closure function. If there is no external variable bound to it, then the function cannot be regarded as a closure function.

So how do you know how many external reference variables a closure function has? Look at the following code.


##
def outer():
  x = 1
  def inner():
    print("x=%s" %x)
    print("inner func excuted")
  print("outer func excuted")
  return inner # 返回内部函数名
  
outer()

The results show that inside the inner, references Two external local variables. If a non-local variable is referenced, then the output here is None.

Characteristics of the closure function:

1. Has its own scope 2. Delayed calculation

Then closure What does the package function do? We clearly know that when the closure function is defined, it will be bound to an external environment. This whole thing can be regarded as a closure function, then we can use this binding feature to complete certain special functions.

Example 3: Download the page source code based on the incoming URL

def outer():
  x = 1
  y = 2

  def inner():
    print("x= %s" %x)
    print("y= %s" %y)

  print(inner.closure)
  return inner

outer()

######输出结果#######
(<cell at 0x000000DF9EA965B8: int object at 0x000000006FC2B440>, <cell at 0x000000DF9EA965E8: int object at 0x000000006FC2B460>)

Some people may say that this does not meet the conditions of the closure function! I did not reference non-global external variables. In fact, this is not the case. As we said before, as long as the variables inside the function belong to the function. Then I am at index(url), this url also belongs inside the function, but we have omitted one step, so the above function is also a closure function.

4. Decorator


With the above foundation, it is easy to understand the decorator.

Decorator: The external function passed in is decorated Function name, the internal function returns the decorated function name.

Features: 1. Does not modify the calling method of the decorated function 2. Does not modify the source code of the decorated function

a. Parameterless decorator

There are the following examples, We need to calculate the execution time of the code.


import time, random

def index():
  time.sleep(random.randrange(1, 5))
  print("welcome to index page")

  根据装饰器的特点,我们不能对index()进行任何修改,而且调用方式也不能变。这时候,我们就可以使用装饰器来完成如上功能.


import time, random

def outer(func): # 将index的地址传递给func
  def inner():
    start_time = time.time()
    func()  # fun = index 即func保存了外部index函数的地址
    end_time = time.time()
    print("运行时间为%s"%(end_time - start_time))
  return inner # 返回inner的地址

def index():
  time.sleep(random.randrange(1, 5))
  print("welcome to index page")

index = outer(index) # 这里返回的是inner的地址,并重新赋值给index

index()

  但是,有些情况,被装饰的函数需要传递参数进去,有些函数又不需要参数,那么如何来处理这种变参数函数呢?下面来看看有参数装饰器的使用情况.

  b.有参装饰器


def outer(func): # 将index的地址传递给func
  def inner(*args, **kwargs):
    start_time = time.time()
    func(*args, **kwargs)  # fun = index 即func保存了外部index函数的地址
    end_time = time.time()
    print("运行时间为%s"%(end_time - start_time))
  return inner # 返回inner的地址

  下面来说说一些其他情况的实例。

   如果被装饰的函数有返回值


def timmer(func):
  def wrapper(*args,**kwargs):
    start_time = time.time()
    res=func(*args,**kwargs) #res来接收home函数的返回值
    stop_time=time.time()
    print(&#39;run time is %s&#39; %(stop_time-start_time))
    return res 
  return wrapper

def home(name):
  time.sleep(random.randrange(1,3))
  print(&#39;welecome to %s HOME page&#39; %name)
  return 123123123123123123123123123123123123123123

  这里补充一点,加入我们要执行被装饰后的函数,那么应该是如下调用方式:

  home = timmer(home)  # 等式右边返回的是wrapper的内存地址,再将其赋值给home,这里的home不在是原来的的那个函数,而是被装饰以后的函数了。像home = timmer(home)这样的写法,python给我们提供了一个便捷的方式------语法糖@.以后我们再要在被装饰的函数之前写上@timmer,它的效果就和home = timmer(home)是一样的。

  如果一个函数被多个装饰器装饰,那么执行顺序是怎样的。


import time
import random

def timmer(func):
  def wrapper():
    start_time = time.time()
    func()
    stop_time=time.time()
    print(&#39;run time is %s&#39; %(stop_time-start_time))
  return wrapper
def auth(func):
  def deco():
    name=input(&#39;name: &#39;)
    password=input(&#39;password: &#39;)
    if name == &#39;egon&#39; and password == &#39;123&#39;:
      print(&#39;login successful&#39;)
      func() #wrapper()
    else:
      print(&#39;login err&#39;)
  return deco

@auth  # index = auth(timmer(index))         
@timmer # index = timmer(index)
def index():
 
  time.sleep(3)
  print(&#39;welecome to index page&#39;)

index()

  实验结果表明,多个装饰器装饰一个函数,其执行顺序是从下往上。

  关于装饰器,还有一些高级用法,有兴趣的可以自己研究研究。

The above is the detailed content of Sample code sharing that allows you to have an in-depth understanding of python decorators. For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn