2009-01-20 25 views
25

Xét đoạn mã sau:Mô phỏng một 'tĩnh địa phương' biến trong python

def CalcSomething(a): 
    if CalcSomething._cache.has_key(a): 
     return CalcSomething._cache[a] 
    CalcSomething._cache[a] = ReallyCalc(a) 
    return CalcSomething._cache[a] 

CalcSomething._cache = { } 

Đây là cách đơn giản nhất tôi có thể nghĩ ra để mô phỏng một 'tĩnh địa phương' biến trong python.
gì làm tôi bực mình là CalcSomething._cache được đề cập bên ngoài định nghĩa của chức năng, nhưng thay thế sẽ là một cái gì đó như thế:

if not hasattr(CalcSomething, "_cache"): 
    setattr(CalcSomething, "_cache", { }) 

bên trong định nghĩa của chức năng, đó là thực sự cồng kềnh.

Có cách nào thanh lịch hơn không?

[EDIT]
Chỉ cần làm rõ, câu hỏi này không phải về bộ đệm chức năng cục bộ, như ví dụ trên có thể đề xuất. Dưới đây là một ví dụ ngắn khác về một 'địa phương tĩnh' có thể hữu ích:

def ParseString(s): 
    return ParseString._parser.parse(s) 
# Create a Parser object once, which will be used for all parsings. 
# Assuming a Parser object is heave on resources, for the sake of this example. 
ParseString._parser = Parser() 
+0

BTW, bạn mi ght muốn đặt câu hỏi của bạn theo cách khác. Bạn không tìm kiếm biến tĩnh cục bộ, nhưng để có cách giới thiệu ghi nhớ cho hàm của bạn. –

+0

ví dụ thứ hai của bạn tệ hơn ví dụ thứ nhất. Bạn có thể trả về datetime.datetime.strptime (dts, "% m-% d-% Y% H:% M:% S") vì strptime là một phương thức lớp tạo một đối tượng datetime mới. Có thực sự không cần một đối tượng datetime được tạo ra. – nosklo

+0

Bạn nói đúng về ví dụ thứ hai. Thay đổi điều đó. –

Trả lời

48

Biến nó thành một đối tượng có thể được gọi (vì đó là những gì nó thực sự là.)

class CalcSomething(object): 
    def __init__(self): 
     self._cache = {} 
    def __call__(self, a): 
     if a not in self._cache: 
      self._cache[a] = self.reallyCalc(a) 
     return self._cache[a] 
    def reallyCalc(self, a): 
     return # a real answer 
calcSomething = CalcSomething() 

Bây giờ bạn có thể sử dụng calcSomething như thể nó là một hàm. Nhưng nó vẫn gọn gàng và độc lập.

+0

+1 - một giải pháp khác ẩn bộ nhớ đệm triển khai. Cách tiếp cận này đã không vượt qua tâm trí của tôi, nhưng nó trông mạnh mẽ hơn trang trí đơn giản của tôi. – Abgan

+0

+1: sử dụng lớp * là * giải pháp. – nosklo

+0

Đối với những người muốn sử dụng callables tùy chỉnh như phương pháp, bạn sẽ phải thực hiện __get__ đúng cách (google cho "python descriptor protocol" để biết thêm về điều này) –

16

Biến nó thành trang trí.

def static_var(var_name, initial_value): 
    def _set_var(obj): 
     setattr(obj, var_name, initial_value) 
     return obj 
    return _set_var 

@static_var("_cache", {}) 
def CalcSomething(a): 
    ... 
+1

Bạn có thực sự coi đó là 'một cách thanh lịch hơn' không? Không, nghiêm túc ;-) –

+0

Tôi sẽ không xem xét các biến tĩnh một mẫu trang nhã để sử dụng trong Python, nhưng trình trang trí ít nhất đóng gói các chi tiết kỹ thuật của cài đặt thuộc tính. –

+0

+1 để sử dụng mát thuộc tính python. @ Oau Oyster: không có ý tưởng tại sao bạn không xem xét điều này thanh lịch. Nếu bạn muốn một cách không hackish để làm điều đó, chỉ cần sử dụng một lớp Calculator với một biến cá thể. Bạn đã yêu cầu tĩnh cục bộ vốn vốn xấu xí. –

11

Hãy xem xét văn bản trang trí mà sẽ duy trì bộ nhớ cache và chức năng của bạn sẽ không bị ô nhiễm bởi mã bộ nhớ đệm:

def cacheResults(aFunc): 
    '''This decorator funcion binds a map between the tuple of arguments 
     and results computed by aFunc for those arguments''' 
    def cachedFunc(*args): 
     if not hasattr(aFunc, '_cache'): 
      aFunc._cache = {} 
     if args in aFunc._cache: 
      return aFunc._cache[args] 
     newVal = aFunc(*args) 
     aFunc._cache[args] = newVal 
     return newVal 
    return cachedFunc 

@cacheResults 
def ReallyCalc(a): 
    '''This function does only actual computation''' 
    return pow(a, 42) 

Có lẽ nó không nhìn tuyệt vời lúc đầu, nhưng bạn có thể sử dụng bất cứ nơi nào bạn cacheResults() không cần tham số từ khóa. Nó có thể tạo ra trang trí tương tự mà sẽ làm việc cũng cho params từ khóa, nhưng điều đó dường như không cần thiết thời gian này.

+0

Mặc dù đây không phải là mục đích của câu hỏi của tôi (xem làm rõ ở đó), đây là một chương trình đẹp để thực hiện bộ nhớ cache cục bộ. Cảm ơn vì điều đó. –

+0

Bạn có thể loại bỏ điều kiện 'if not hasattr' ... bằng cách tạo biến cục bộ' _cache' 'cacheResults'. – GingerPlusPlus

3

Một tùy chọn là lạm dụng thông số mặc định. nghĩa là:

def CalcSomething(a, _cache={}): 
    if _cache.has_key(a): 

Điều này có lợi thế là bạn không cần phải đủ điều kiện tên và sẽ nhanh chóng truy cập vào các biến thay vì thực hiện hai lần tra cứu chậm. Tuy nhiên nó vẫn có vấn đề mà nó được đề cập bên ngoài chức năng (trên thực tế nó tồi tệ hơn kể từ bây giờ trong chữ ký chức năng.)

Để ngăn chặn điều này, một giải pháp tốt hơn là bao bọc chức năng trong bao đóng chứa số liệu thống kê của bạn :

@apply 
def CalcSomething(): 
    cache = {} # statics go here 

    def CalcSomething(a): 
     if cache.has_key(a): 
      return cache[a] 
     cache[a] = ReallyCalc(a) 
     return cache[a] 
    return CalcSomething 
+0

Thật không may, áp dụng đã bị xóa khỏi Python 3.0. Tôi cũng tìm thấy một số ít các trường hợp tương tự khi nó ngắn, đơn giản và hữu ích. –

+0

Tôi không nghĩ đó là cách sử dụng tốt. Rõ ràng hơn là chỉ cần gọi hàm. –

4

Giải pháp được đề xuất bởi S.Lott là giải pháp tôi cũng đề xuất.

Có hữu ích "memoize" trang trí xung quanh, quá, như:

Với tất cả những gì, tôi đang cung cấp một thay thế cho nỗ lực ban đầu của bạn tại một và "địa phương tĩnh", độc lập:

def calc_something(a): 

    try: 
     return calc_something._cache[a] 
    except AttributeError: # _cache is not there 
     calc_something._cache= {} 
    except KeyError: # the result is not there 
     pass 

    # compute result here 

    calc_something._cache[a]= result 
    return result 
Các vấn đề liên quan