2012-05-31 25 views
5

Ý tưởng của tôi là tạo các đối tượng hàm cụ thể có thể được cộng/trừ/... với nhau, trả về đối tượng hàm mới có cùng thuộc tính. Mã ví dụ này, hy vọng, thể hiện ý tưởng:Lập trình tạo các phương thức đặc biệt số học bằng Python, (aka hàm factory HOWTO)

from FuncObj import Func 

# create some functions 
quad = Func(lambda x: x**2) 
cube = Func(lambda x: x**3) 

# now combine functions as you like 
plus = quad + cube 
minus = quad - cube 
other = quad * quad/cube 

# and these can be called 
plus(1) + minus(32) * other(5) 

Tôi đã viết mã sau đây, được hy vọng nhận xét và tài liệu đủ để giải thích những gì tôi muốn đạt được.

import operator 

class GenericFunction(object): 
    """ Base class providing arithmetic special methods. 
     Use derived class which must implement the 
     __call__ method. 
    """ 

    # this way of defining special methods works well 
    def __add__(self, operand): 
     """ This is an example of a special method i want to implement. """ 
     obj = GenericFunction() 
     # this is a trick from Alex Martelli at 
     # http://stackoverflow.com/questions/1705928/problem-with-making-object-callable-in-python 
     # to allow per-instance __call__ methods 
     obj.__class__ = type(obj.__class__.__name__, (obj.__class__,), {}) 
     obj.__class__.__call__ = lambda s, ti: self(ti) + operand(ti) 
     return obj 

    # on the other hand this factory function seems buggy 
    def _method_factory(operation, name): 
     """ Method factory. 
     Parameters 
     ---------- 
     op : callable 
      an arithmetic operator from the operator module 
     name : str 
      the name of the special method that will be created 
     Returns 
     ------- 
     method : callable 
      the __***__ special method 
     """ 
     def method(s, operand): 
      obj = GenericFunction() 
      obj.__class__ = type(obj.__class__.__name__, (obj.__class__,), {}) 
      obj.__class__.__call__ = lambda s, ti: operation(s(ti), operand(ti)) 
      return obj 
     return method 

    __sub__ = _method_factory(operator.__sub__, '__sub__') 
    __mul__ = _method_factory(operator.__mul__, '__mul__') 
    __truediv__ = _method_factory(operator.__truediv__, '__div__') 


class Func(GenericFunction): 
    """ A customizable callable object. 
     Parameters 
     ---------- 
     func : callable 
    """ 
    def __init__(self, func): 
     self.func = func 

    def __call__(self, *args): 
     return self.func(*args) 


if __name__ == '__main__': 

    # create some functions 
    quad = Func(lambda x: x**2) 
    cube = Func(lambda x: x**3) 

    # now combine functions 
    poly_plus = quad + cube 
    poly_minus = quad - cube 

    # this is the expected behaviour, and it works well 
    # since the __add__ method is defined correctly. 
    assert quad(1) + cube(1) == poly_plus(1) 

    # this, and the others with * and/result in a "maximum recursion depth exceeded" 
    assert quad(1) - cube(1) == poly_minus(1) 

Tôi nghĩ rằng tôi thiếu điều gì đó quan trọng nhưng tôi không thể nhìn thấy nó.

EDIT

Sau Dietrich câu trả lời tôi quên đề cập đến một trường hợp góc. Giả sử tôi muốn phân lớp GenericInput và tôi cần phải tùy chỉnh gọi phương thức__, mà không truyền một hàm gọi đến hàm tạo. Tôi phải ví dụ, (thực sự đây là mã mà tôi đã đăng câu hỏi này).

class NoiseInput(GenericInput): 
    def __init__(self, sigma, a, b, t): 
     """ A band-pass noisy input. """ 
     self._noise = lfilter(b, a, np.random.normal(0, 1, len(t))) 
     self._noise *= sigma/self._noise.std() 
     self._spline = InterpolatedUnivariateSpline(t, self._noise, k=2) 

    def __call__(self, ti): 
     """ Compute value of the input at a given time. """ 
     return self._spline(ti) 

class SineInput(GenericInput): 
    def __init__(self, A, fc): 
     self.A = A 
     self.fc = fc 

    def __call__(self, ti): 
     return self.A*np.sin(2*np.pi*ti*self.fc) 

Trong trường hợp này, còn một số việc phải làm.

+0

trong ví dụ của bạn, không 'cộng (1)' có nghĩa là 'quad (1) + khối lập phương (1) '? – inspectorG4dget

+0

@ inspectorG4dget Có, đó là hành vi dự định. – Davide

Trả lời

2

Có rất nhiều mã ở đây không cần phải tồn tại và nó phức tạp hơn mức cần thiết.

Ví dụ: thuộc tính __class__ là một trong những thuộc tính "ma thuật". Thuộc tính ma thuật là đặc biệt và chỉ cần được sử dụng trong các trường hợp đặc biệt, như khi bạn đang sử dụng lập trình meta. Không cần tạo một lớp theo mã ở đây.

Ví dụ khác là lớp Func trong mã của bạn, điều này thực sự không làm gì cả. Bạn có thể thay thế an toàn bằng:

def Func(x): 
    return x 

Vì vậy, bạn có vấn đề ngược lại: bạn không "thiếu" bất cứ điều gì, bạn có quá nhiều.

class Func(object): 
    def __init__(self, func): 
     self._func = func 
    def __call__(self, x): 
     return self._func(x) 
    def __mul__(self, other): 
     return Func(lambda x: self(x) * other(x)) 
    def __add__(self, other): 
     return Func(lambda x: self(x) + other(x)) 
    def __sub__(self, other): 
     return Func(lambda x: self(x) - other(x)) 

Lưu ý rằng đây là không theo cách truyền thống của việc hoàn thành một vấn đề như vậy. Theo truyền thống, người ta tránh lambdas và sử dụng một cây biểu hiện ở đây. Lợi thế của việc sử dụng một cây biểu thức là các biểu thức kết quả có thể được thao tác đại số. Ví dụ, bạn có thể giải quyết chúng, tính toán các dẫn xuất chính xác, hoặc in chúng ra dưới dạng phương trình.

0

Tôi giả định rằng bạn muốn f(x ** 2) + f(x ** 3) trả về một hàm x**2 + x**3? Bạn có thể thử điều này:

class Func: 
    def __init__(self, func): 
     self._func = func 

    def __call__(self, *args): 
     return self._func(*args) 

    def __add__(self, other): 
     def result(*args): 
      return self._func(*args) + other(*args) 
     return Func(result) 
    __radd__ = __add__ 

    def __mul__(self, other): 
     def result(*args): 
      return self._func(*args) * other(*args) 
     return Func(result) 
    __rmul__ = __mul__ 

    # etc... 

làm việc cho tôi và đơn giản hơn nhiều so với những gì bạn có.

EDIT:

Bạn có thể có thể thậm chí không bận tâm với self._func cuộc gọi trong các phương pháp số học và chỉ cần gọi self trực tiếp.

Các vấn đề liên quan