2012-08-02 29 views
5

W rzeczywistości tytuł nie odzwierciedla dokładnie tego pytania, które chcę zadać. Mój cel jest taki: piszę niektóre funkcje kreślenia za pomocą matplotlib. Mam szereg funkcji przeznaczonych do różnych celów drukowania. jak line_plot() dla linii, bar_plot() na pasek itp na przykład:użyć python decorator, aby automatycznie zastąpić domyślną wartość argumentu funkcji?

import matplotlib.pyplot as plt 
def line_plot(axes=None,x=None,y=None): 
    if axes==None: 
     fig=plt.figure() 
     axes=fig.add_subplot(111) 
    else: 
     pass 
    axes.plot(x,y) 

def bar_plot(axes=None,x=None,y=None): 
    if axes==None: 
     fig=plt.figure() 
     axes=fig.add_subplot(111) 
    else: 
     pass 
    axes.bar(left=x,height=y) 

Jednak problemem jest to, dla każdej funkcji, która jest zdefiniowana, muszę powtórzyć tę część kodu:

if axes==None: 
     fig=plt.figure() 
     axes=fig.add_subplot(111) 
    else: 
     pass 

Czy istnieje sposób, jak przy użyciu dekoratora, który mogę zastosować przed zdefiniowaniem funkcji kreślenia, która automatycznie wykona powtarzającą się część kodu? Dlatego nie muszę ich powtarzać za każdym razem.

jedną z możliwych opcji jest zdefiniować funkcję tak:

def check_axes(axes): 
    if axes==None: 
     fig=plt.figure() 
     axes=fig.add_subplot(111) 
     return axes 
    else: 
     return axes 

Następnie przykłady będą jak:

import matplotlib.pyplot as plt  
def line_plot(axes=None,x=None,y=None): 
    axes=check_axes(axes) 
    axes.plot(x,y) 

def bar_plot(axes=None,x=None,y=None): 
    axes=check_axes(axes) 
    axes.bar(left=x,height=y) 

ale jest lepiej/czyste/bardziej pythonic sposób? Myślę, że mogę użyć dekoratora, ale nie zrozumiałem. czy ktokolwiek mógłby dać jakiś pomysł?

Dzięki!

+2

Myślę, że twoje ostatnie rozwiązanie jest całkiem dobre. Funkcje są efektywnymi, sprawdzonymi sposobami budowania kodu. Podejrzewam, że dekorator komplikowałby niepotrzebnie rzeczy. –

+0

Być może możesz stworzyć klasę osi z testem w swoim init – zenpoy

Odpowiedz

7

Oto jak to zrobić za pomocą dekoratora:

import matplotlib.pyplot as plt  

def check_axes(plot_fn): 
    def _check_axes_wrapped_plot_fn(axes=None, x=None, y=None): 
     if not axes: 
      fig = plt.figure() 
      axes = fig.add_subplot(111) 
      return plot_fn(axes, x, y) 
     else: 
      return plot_fn(axes, x, y) 
    return _check_axes_wrapped_plot_fn 

@check_axes 
def line_plot(axes, x=None, y=None): 
    axes.plot(x, y) 

@check_axes 
def bar_plot(axes, x=None, y=None): 
    axes.bar(left=x, height=y) 

Jak to działa: składnia @check_axes redefiniuje nazwę zdobione funkcji, na przykład line_plot to nowa funkcja stworzona przez dekoratora, tj. _check_axes_wrapped_plot_fn. Ta "zapakowana" funkcja obsługuje logikę sprawdzania axes, a następnie wywołuje funkcję oryginalnego wydruku.

Jeśli chcesz check_axes móc ozdobić dowolną funkcję działki, która pobiera axes jako pierwszy argument, a nie tylko te, które również wziąć tylko x i y argumentów można użyć wygodnej składni Pythona * dla list dowolnych argumentów:

def check_axes(plot_fn): 
    def _check_axes_wrapped_plot_fn(axes=None, *args): 
     if not axes: 
      fig = plt.figure() 
      axes = fig.add_subplot(111) 
      return plot_fn(axes, *args) # pass all args after axes 
     else: 
      return plot_fn(axes, *args) # pass all args after axes 
    return _check_axes_wrapped_plot_fn 

Teraz, czy którykolwiek z nich jest "lepszy/czysty/bardziej Pythoniczny" jest prawdopodobnie kwestią dyskusji i zależy od szerszego kontekstu.

Przy okazji, w duchu bycia "bardziej Pythonicznym", sformatowałem Twój kod tak, aby był bliżej przewodnika stylu PEP8. Zwróć uwagę na spacje po przecinkach w listach parametrów, spacje wokół operatora przypisania = (ale nie wtedy, gdy = użyte są dla parametrów słów kluczowych funkcji), i powiedzmy not axes zamiast.

+1

Cześć dzięki Ghopper21 za odpowiedź. To właśnie chcę zobaczyć. Tak, czy to bardziej pythonic powinien brać pod uwagę większy obraz. Dziękuję również za ponowne sformatowanie kodu, a sformatowany wygląda lepiej :) – wiswit