2009-03-13 14 views
14

Nếu tôi có một chức năng:Có thể thay thế một trình trang trí hàm/phương thức trong thời gian chạy không? [Python]


@aDecorator 
def myfunc1(): 
    # do something here 

if __name__ = "__main__": 
    # this will call the function and will use the decorator @aDecorator 
    myfunc1() 
    # now I want the @aDecorator to be replaced with the decorator @otherDecorator 
    # so that when this code executes, the function no longer goes through 
    # @aDecorator, but instead through @otherDecorator. How can I do this? 
    myfunc1() 

Có thể thay thế một trang trí trong thời gian chạy?

Trả lời

2

Tôi không biết nếu có một cách để "thay thế" một trang trí một khi nó đã được áp dụng, nhưng tôi đoán rằng có lẽ không có, bởi vì chức năng đã được thay đổi.

Bạn có thể, dù sao, áp dụng một trang trí trong thời gian chạy dựa trên một số điều kiện:

#!/usr/bin/env python 

class PrintCallInfo: 
    def __init__(self,f): 
     self.f = f 
    def __call__(self,*args,**kwargs): 
     print "-->",self.f.__name__,args,kwargs 
     r = self.f(*args,**kwargs) 
     print "<--",self.f.__name__,"returned: ",r 
     return r 

# the condition to modify the function... 
some_condition=True 

def my_decorator(f): 
    if (some_condition): # modify the function 
     return PrintCallInfo(f) 
    else: # leave it as it is 
     return f 

@my_decorator 
def foo(): 
    print "foo" 

@my_decorator 
def bar(s): 
    print "hello",s 
    return s 

@my_decorator 
def foobar(x=1,y=2): 
    print x,y 
    return x + y 

foo() 
bar("world") 
foobar(y=5) 
-1

Nếu trình trang trí là một hàm, chỉ cần thay thế nó.

aDecorator = otherDecorator 
+1

Nhưng điều này sẽ không thay đổi myfunc1(). –

+0

Tôi đã thử sử dụng mã của bạn, hàm vẫn thực thi với trình trang trí ban đầu. – Geo

+0

wow .. không mong đợi điều này xảy ra. – miya

0

Nếu bạn muốn explicitely thay đổi trang trí, bạn cũng có thể chọn một cách tiếp cận rõ ràng hơn thay vì tạo một chức năng trang trí:

deco1(myfunc1, arg1, arg2) 
deco2(myfunc1, arg2, arg3) 

deco1() và deco2() sẽ áp dụng các chức năng trang trí của bạn cung cấp và gọi myfunc1() với các đối số.

+0

Có, tôi có thể đã sử dụng một hàm có chức năng như là tham số đầu tiên, hàm của tôi là tham số thứ hai, tiếp theo là các đối số và có cách thực hiện khá chung chung.Tuy nhiên, tôi đã quan tâm, nếu tôi có thể thay thế một trang trí bằng chính mình. Tôi muốn "chơi" với hành vi của các lớp khác – Geo

13

Như Miya đã đề cập, bạn có thể thay thế trang trí bằng một hàm khác bất kỳ điểm nào trước khi trình thông dịch nhận được khai báo hàm đó. Tuy nhiên, khi trang trí được áp dụng cho hàm, tôi không nghĩ rằng có một cách để thay thế động trang trí bằng một công cụ khác. Ví dụ:

@aDecorator 
def myfunc1(): 
    pass 

# Oops! I didn't want that decorator after all! 

myfunc1 = bDecorator(myfunc1) 

Sẽ không hoạt động, vì myfunc1 không còn là hàm bạn đã xác định ban đầu; nó đã được bọc rồi. Phương pháp tốt nhất ở đây là để tự áp dụng trang trí, oldskool-phong cách, tức là:

def myfunc1(): 
    pass 

myfunc2 = aDecorator(myfunc1) 
myfunc3 = bDecorator(myfunc1) 

Chỉnh sửa: Hoặc, để được rõ ràng hơn một chút,

def _tempFunc(): 
    pass 

myfunc1 = aDecorator(_tempFunc) 
myfunc1() 
myfunc1 = bDecorator(_tempFunc) 
myfunc1() 
+0

+1: aDecorate (func1) và bDecorate (func1) –

3

Dưới đây là một tuyệt vời recipe to get you started. Về cơ bản, ý tưởng là để vượt qua một thể hiện lớp vào trang trí. Sau đó bạn có thể thiết lập các thuộc tính trên cá thể lớp (làm cho nó thành một Borg nếu bạn thích) và sử dụng nó để điều khiển hành vi của trình trang trí.

Dưới đây là một ví dụ:

class Foo: 
    def __init__(self, do_apply): 
     self.do_apply = do_apply 

def dec(foo): 
    def wrap(f): 
     def func(*args, **kwargs): 
      if foo.do_apply: 
       # Do something! 
       pass 
      return f(*args, **kwargs) 
     return func 
    return wrap 

foo = Foo(False) 
@dec(foo) 
def bar(x): 
    return x 

bar('bar') 
foo.do_apply = True 
# Decorator now active! 
bar('baz') 

Đương nhiên, bạn cũng có thể kết hợp các "trang trí trang trí" để bảo tồn chữ ký vv

1

chắc - bạn có thể nhận được các đối tượng chức năng và làm bất cứ điều gì bạn muốn với nó:

# Bypass a decorator 

import types 

class decorator_test(object): 

    def __init__(self, f): 
     self.f = f 

    def __call__(self): 
     print "In decorator ... entering: ", self.f.__name__ 
     self.f() 
     print "In decorator ... exiting: ", self.f.__name__ 


@decorator_test 
def func1(): 
    print "inside func1()" 

print "\nCalling func1 with decorator..." 
func1() 

print "\nBypassing decorator..." 
for value in func1.__dict__.values(): 
    if isinstance(value, types.FunctionType) and value.func_name == "func1": 
     value.__call__() 
+0

Điều này giả định quyền kiểm soát trang trí ban đầu. Hãy tưởng tượng trang trí 'def aDecorator (f): trả về lambda * args, ** kwargs:" HAHAHAHAHAHAHA "'. Nói chung, đây không phải là một cách tiếp cận hợp lệ. Nếu bạn __do__ xảy ra để có quyền kiểm soát của trang trí ban đầu, sau đó có sạch hơn, nhiều cách thành ngữ để làm điều này. – aaronasterling

0

tôi biết đó là một chủ đề cũ, nhưng tôi đã vui vẻ làm điều này

def change_deco(name, deco, placeholder=' #'): 
with open(name + '.py', 'r') as file: 
    lines = file.readlines() 
for idx, string in enumerate(lines): 
    if placeholder in string and repr(placeholder) not in string: 
     lines[idx] = f' @{deco}\r\n' 
exec(''.join(lines)) 
return locals()[name] 
Các vấn đề liên quan