2011-11-05 33 views
5

Tôi đang viết mô-đun cung cấp một hàm và cần bước khởi tạo, tuy nhiên do các hạn chế nhất định mà tôi cần khởi tạo khi gọi đầu tiên, vì vậy tôi đang tìm kiếm thành ngữ thích hợp trong python. tôi để loại bỏ điều kiện.Thay đổi thực thi hàm trong Python

#with conditional 
module.py 
initialized = False 
def function(*args): 
    if not initialized: initialize() 
    do_the_thing(*args) 

Tôi muốn thoát khỏi điều đó có điều kiện với một cái gì đó như thế này (nó không hoạt động):

#with no conditional 
module.py 
def function(*args): 
    initialize() 
    do_the_thing(*args) 
    function = do_the_thing 

Tôi nhận ra rằng tôi không thể chỉ sử dụng tên trong module và thay đổi chúng tại thời gian chạy vì các mô-đun sử dụng from module import function sẽ không bao giờ bị ảnh hưởng với một mô-đun function=other_fun bên trong mô-đun.
Vì vậy, có bất kỳ thành ngữ pythonic mà có thể làm điều này đúng cách?

+0

google cho 'python trang trí' – Triptych

+0

tôi không thấy làm thế nào một trang trí có thể làm điều này, có điều kiện vẫn còn ở đó nếu bạn kiểm tra giải pháp @graphox, bằng cách sử dụng một máy phát điện hoạt động như mong đợi mặc dù nó cảm thấy một chút lạ. –

Trả lời

8

Cách gì-fancy (trong những phương pháp tôi gửi ở đây, đây có lẽ là cách tốt nhất để làm điều đó):

module.py:

def initialize(): 
    print('initialize') 
def do_the_thing(args): 
    print('doing things',args) 
def function(args): 
    _function(args) 
def firsttime(args): 
    global _function 
    initialize() 
    do_the_thing(args) 
    _function=do_the_thing 
_function=firsttime 

Ý tưởng rất đơn giản: bạn chỉ cần thêm một lớp indirection. function luôn gọi _function, nhưng _function điểm đầu tiên tại firsttime, sau đó mãi mãi sau do_the_thing.

test.py:

from module import function 
function(1) 
function([2,3]) 

Chạy test.py sản lượng

initialize 
('doing things', 1) 
('doing things', [2, 3]) 

Suy nghĩ đầu tiên của tôi là sử dụng một máy phát điện, nhưng, như Triptych chỉ ra, có không có cách nào để chuyển args cho hàm nếu bạn sử dụng một trình tạo. Vì vậy, ...

đây là một cách sử dụng một coroutine (trong đó, không giống như một máy phát điện, cho phép bạn gửi args để - cũng như nhận được các giá trị từ - các coroutine):

module.py :

def coroutine(func): 
    # http://www.dabeaz.com/coroutines/index.html 
    def start(*args,**kwargs): 
     cr = func(*args,**kwargs) 
     cr.next() 
     return cr 
    return start 

def initialize(): 
    print('initialize') 

def do_the_thing(*args, **kwargs): 
    print('doing things', args, kwargs) 
    return ('result', args) 

@coroutine 
def _function(): 
    args, kwargs = (yield) 
    initialize() 
    while True: 
     args, kwargs = (yield do_the_thing(*args, **kwargs)) 
_function = _function().send 
def function(*args, **kwargs): 
    # This is purely to overcome the limitation that send can only accept 1 argument 
    return _function((args,kwargs)) 

Chạy

print(function(1, x = 2)) 
print(function([2, 3])) 

mang

initialize 
('doing things', (1,), {'x': 2}) 
('result', (1,)) 
('doing things', ([2, 3],), {}) 
('result', ([2, 3],)) 
+0

điều này chạy bước khởi tạo tại thời gian tải mô-đun, không phải lúc gọi đầu tiên như OP yêu cầu ... – Triptych

+0

@Triptych: Tôi không tin đó là sự thật. Bạn đã thử nghiệm nó chưa? – unutbu

+0

ah misread 'next' như thể nó là' next() '. Xấu của tôi ... Vẫn còn tò mò làm thế nào bạn muốn viết để có đối số. Không phải 'next' phải là một hàm zero-argument? Có vẻ như ... lạ với tôi. – Triptych

2

Bạn cũng có thể sử dụng một trang trí, nó có thể linh hoạt hơn nếu bạn có một số chức năng để khởi tạo:

import functools  

def initialize(initialize_function): 
    def wrap(fn): 
     fn.initialized = False 
     @functools.wraps(fn) 
     def wrapper(*args, **kwargs): 
      if not fn.initialized: 
       initialize_function() 
       fn.initialized = True 
      return fn(*args, **kwargs) 
     return wrapper 
    return wrap 

def initialize_first_fn(): 
    print('first function initalized') 

def initialize_second_fn(): 
    print('second function initalized') 

@initialize(initialize_first_fn) 
def first_fn(*args): 
    print(*args) 

@initialize(initialize_second_fn) 
def second_fn(*args): 
    print(*args) 


>>>first_fn('initialize', 'please') 
first function initalized 
initialize please 
>>> first_fn('it works') 
it works 
>>> second_fn('initialize', 'please') 
second function initalized 
initialize please 
>>> second_fn('it also works') 
it also works 

(cần phải được cải thiện phụ thuộc vào nhu cầu của bạn)

+0

Hmm, mã trình bao bọc có thực hiện mọi cuộc gọi không? Ý tôi là, điều kiện sẽ vẫn ở đó trong cuộc gọi thứ hai, chỉ cần đánh giá là sai, phải không? –

+1

Tôi muốn sử dụng '@ functools.wraps (fn)' trên 'def wrapper'. Bằng cách đó, hàm bạn lấy lại trông giống như hàm ban đầu ('first_fn', v.v.). Tôi cũng có thể sử dụng một trang trí dựa trên lớp học. –

+0

Tôi vừa thử nghiệm nó và thực sự, "nếu không khởi tạo" vẫn xảy ra mọi cuộc gọi, đó là những gì tôi muốn loại bỏ. :( –

3

tôi mất về điều này: bạn không nên làm điều này.

Trong trường hợp bạn cần một "hàm" có "bước khởi tạo" và chế độ làm việc bình thường, bạn cần một cá thể lớp.Đừng cố gắng để trở nên thông minh, độc giả tương lai của mã của bạn sẽ ghét bạn vì điều đó :)

# module.py 

class ThingDoer(object): 
    def __init__(self): 
     # initialize 

    def do_the_thing(self, *args): 
     # ... 
Các vấn đề liên quan