>백엔드 개발 >파이썬 튜토리얼 >파이썬 데코레이터 소개

파이썬 데코레이터 소개

巴扎黑
巴扎黑원래의
2017-08-07 17:57:261418검색

Python의 데코레이터는 Python에 입문하는 데 장애물이 됩니다. Python의 데코레이터 개념은 종종 사람들을 혼란스럽게 합니다. 따라서 오늘은 Python의 데코레이터를 분석해 보겠습니다

1. Scope

Python에는 전역 범위와 로컬 범위라는 두 가지 유형의 범위가 있습니다.

전역 범위는 파일 수준에서 정의된 변수 및 함수 이름입니다. 로컬 범위는 정의 함수 내부에 있습니다.

범위와 관련하여 다음 두 가지 사항을 이해해야 합니다. a. 로컬로 정의된 변수는 전역적으로 액세스할 수 없습니다. b. 전역으로 정의된 변수는 로컬로 액세스할 수 있지만 전역으로 정의된 변수는 수정할 수 없습니다.

다음 예를 살펴보겠습니다.


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

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

변수 x가 로컬로 정의되지 않은 경우 함수는 다음을 검색합니다. 도메인 문제와 관련하여 두 가지 사항만 기억하면 됩니다. 전역 변수는 어디에서나 참조될 수 있습니다. 하지만 수정은 전역적으로만 수행할 수 있습니다. 필요한 변수가 로컬에서 발견되지 않으면 외부에서 검색되며 발견되지 않으면 오류가 보고됩니다.

2. 고급 기능

함수 이름이 실제로 메모리 공간의 주소를 가리키는 것임을 알고 있으므로 이 기능을 사용할 수 있습니다.

 a 함수 이름을 값으로 사용할 수 있습니다

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. 함수 이름을 반환 값으로 사용할 수 있습니다


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. 함수 이름을 매개 변수로 사용할 수 있습니다

.

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

s = outer()
print(s)

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

그러면 위의 두 가지 조건이 모두 충족됩니다.

3. 클로저 함수

클로저 함수는 두 가지 조건을 충족해야 합니다. 1. 함수 내부에 정의된 함수 2. 전역 범위가 아닌 외부 범위를 포함합니다. 다음은 몇 가지 예를 통해 클로저 함수에 대한 참조입니다.

예 1: 다음은 함수 내부의 함수만 정의하지만 클로저 함수는 아닙니다.

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

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

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

index func

예 2: 다음은 함수 내부에 정의되어 있으며 외부 변수도 참조합니다. 두 번째 항목이 충족되지 않았음을 발견했을 것입니다. 예, 여기서 변수 x는 외부적으로 작동하는 변수가 아니라 전역 변수입니다. 도메인. 다음 예를 살펴보겠습니다.

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

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

 분명히 위의 예는 클로저 함수의 조건을 만족합니다. 이제 클로저 함수로서 위의 두 가지 조건을 충족해야 하며 둘 중 어느 것도 제거될 수 없다는 것을 알아야 합니다. 하지만 일반적인 상황에서는 클로저 함수에 값을 반환하게 됩니다. 여기서는 그 이유에 대해 설명하지 않겠습니다. 다음 내용에서는 이 반환 값의 사용을 살펴보겠습니다.

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

이제 클로저를 정의해 보겠습니다. 기능. 함수와 관련 참조 환경으로 구성된 엔터티입니다. 심층 제약 조건을 구현할 때 참조 환경을 명시적으로 나타내는 것을 생성하고 이를 관련 서브루틴과 함께 묶어 번들이 클로저가 되도록 해야 합니다. 위의 예에서 클로저 함수가 실제로 클로저 함수라고 부르려면 자체 함수와 외부 변수를 포함해야 함을 알 수 있습니다. 바인딩된 외부 변수가 없으면 해당 함수는 클로저 함수로 간주될 수 없습니다.

 그러면 클로저 함수에 외부 참조 변수가 몇 개 있는지 어떻게 알 수 있나요? 아래 코드를 보세요.


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

 결과를 보면 내부에서 두 개의 외부 지역 변수가 참조되는 것으로 나타났습니다. 비지역 변수가 참조되면 여기서 출력은 None입니다.

클로저 함수의 특징:


1. 자체 범위를 갖습니다. 2. 지연된 계산

그래서 클로저 함수는 무엇을 하는 걸까요? , 클로저 함수가 정의되면 외부 환경에 바인딩되어야 합니다. 이 모든 것은 클로저 기능으로 간주될 수 있으며, 이 바인딩 기능을 사용하여 특정 특수 기능을 완성할 수 있습니다.

 예제 3: 들어오는 URL을 기반으로 페이지 소스 코드 다운로드


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

 이것이 클로저 기능의 조건을 충족하지 않는다고 말할 수도 있습니다. 비전역 외부 변수를 참조하지 않았습니다! 사실, 앞서 말했듯이 함수 내부의 변수가 함수에 속하면 그렇지 않습니다. 그런 다음 index(url)에 있습니다. 이 URL도 함수 내부에 속하지만 한 단계를 생략했으므로 위 함수도 클로저 함수입니다.

4. 데코레이터

위의 기초를 이용하면 데코레이터를 쉽게 이해할 수 있습니다.

데코레이터: 외부 함수는 데코레이팅된 함수의 이름을 전달하고, 내부 함수는 데코레이팅된 함수의 이름을 반환합니다.

특징: 1. 데코레이팅된 함수의 호출 방법을 수정하지 않습니다. 2. 데코레이팅된 함수의 소스 코드를 수정하지 않습니다.

a. 매개변수 없는 데코레이터

다음 예에서는 코드 실행을 계산해야 합니다. 시간.

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()

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

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

위 내용은 파이썬 데코레이터 소개의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.