7. Decorator

7.1. Zastosowanie

  • Modify arguments
  • Modify returned value
  • Do things before call
  • Do things after call
  • Avoid calling
  • Modify global state (not a good idea)
  • Metadata

7.1.1. Przykład zastosowania

  • Zagnieżdżone
  • wykonywane od góry
@permission_required(uid=0)
@modyfikuj_sciezke_w_zaleznosci_od_systemu_operacyjnego
@timeout(seconds=10, error_message='za dlugo')
def instaluj_oprogramowanie(sciezka, nazwa_oprogramowania, wersja_paczki):
    pass

7.2. Definicja

def my_decorator(f):
    def wrapper(*args, **kwargs):
        return f(*args, **kwargs)
    return wrapper

# usage

@my_decorator
def func(x):
    return x

7.3. Class Decorators

Code Listing 7.1. Case Study wykorzystania dekotatorów do poprawienia czytelności kodu Flask
class Cache(object):
    def __init__(self, function, max_hits=10, timeout=5):
        self.function = function
        self.max_hits = max_hits
        self.timeout = timeout
        self.cache = {}

    def __call__(self, *args):
        # Here the code returning the correct thing.
        print(self)


def cache_hits(max_hits=10, timeout=5):
    def _cache(function):
        return Cache(function, max_hits, timeout)
    return _cache


@cache_hits
def double(x):
    return x * 2

@cache_hits(max_hits=100, timeout=50)
def double(x):
    return x * 2


double()

Todo

classdecorators

7.3.1. @staticmethod

class Foo:
    def __init__(self, tekst='Jose'):
        self.tekst = tekst

    def hello(self):
        print(f'hello {self.tekst}')

    @staticmethod
    def ehlo():
        print('hello')


# pryzkładowa implementacja
def staticmethod(f):
    def wrapper(*args, **kwargs):
        return f()
    return wrapper

7.3.2. @classmethod

7.4. Przykład

import os
import logging


def if_file_exists(function):

    def check(filename):
        if os.path.exists(filename):
            function(filename)
        else:
            logging.error('File "%(filename)s" does not exists, will not execute!', locals())

    return check


@if_file_exists
def print_file(filename):
    with open(filename) as file:
        content = file.read()
        print(content)


if __name__ == '__main__':
    print_file('/etc/passwd')
    print_file('/tmp/passwd')

7.4.1. Case Study

Code Listing 7.2. Case Study wykorzystania dekotatorów do poprawienia czytelności kodu Flask
from flask import json
from flask import Response
from flask import render_template
from flask import Flask

app = Flask(__name__)


@app.route('/summary')
def summary():
    data = {'first_name': 'Jose', 'last_name': 'Jimenez'}

    return Response(
        response=json.dumps(data),
        status=200,
        mimetype='application/json'
    )

@app.route('/post/<int:post_id>')
def show_post(post_id):
    # show the post with the given id, the id is an integer
    return 'Post %d' % post_id


@app.route('/hello/')
@app.route('/hello/<name>')
def hello(name=None):
    return render_template('hello.html', name=name)
Code Listing 7.3. Case Study wykorzystania dekotatorów do poprawienia czytelności kodu Django
from django.shortcuts import render

def edit_profile(request):
    if not request.user.is_authenticated:
        return render(request, 'myapp/login_error.html')
    else:
        return render(request, 'myapp/edit-profile.html')



from django.contrib.auth.decorators import login_required


@login_required
def edit_profile(request):
    return render(request, 'myapp/edit_profile.html')


@login_required(login_url='/accounts/login/')
def edit_product(request):
    return render(request, 'myapp/edit_product.html')





from django.contrib.auth.mixins import LoginRequiredMixin


class EditProductView(LoginRequiredMixin, TemplateView):
    login_url = '/login/'
    redirect_field_name = 'redirect_to'
    template_name = 'myapp/edit_product.html'

7.5. Zadania kontrolne

7.5.1. Prosty dekorator

  • Program przechodzi przez pliki i katalogi wykorzystując os.walk.
  • Stwórz funkcję, która wypisuje na ekranie nazwę pliku lub katalogu.
  • Stwórz dekorator do funkcji, który przed wyświetleniem jej na ekranie podmieni ścieżkę na bezwzględną (path + filename).