Perintah eksekusi dekorator

96
def make_bold(fn):
    return lambda : "<b>" + fn() + "</b>"

def make_italic(fn):
    return lambda : "<i>" + fn() + "</i>"

@make_bold
@make_italic
def hello():
  return "hello world"

helloHTML = hello()

Keluaran: "<b><i>hello world</i></b>"

Saya kira-kira mengerti tentang dekorator dan cara kerjanya dengan salah satunya di sebagian besar contoh.

Dalam contoh ini, ada 2 di antaranya. Dari output, sepertinya yang @make_italicdieksekusi dulu, baru @make_bold.

Apakah ini berarti bahwa untuk fungsi dekorasi, ini akan menjalankan fungsi terlebih dahulu kemudian bergerak ke atas untuk dekorator lain? Seperti @make_italicdulu @make_bold, lalu sebaliknya.

Jadi ini berarti bahwa ini berbeda dari norma pendekatan top-down di kebanyakan bahasa pemrograman? Hanya untuk kasus dekorator ini? Atau apakah saya salah?

Pemula
sumber
4
ya itu dimulai dari bawah ke atas meneruskan hasil ke berikutnya
Padraic Cunningham
1
Komentar @PadraicCunningham juga merupakan bagian penting dari jawabannya. Punya masalah terkait ( stackoverflow.com/questions/47042196/… )
mengguncang
Saya akan mengatakan itu masih top-down, dalam arti itu a(b(x))top-down (jika Anda membayangkan itu terbagi menjadi 3 baris)
joel

Jawaban:

131

Dekorator membungkus fungsi yang mereka dekorasi. Begitu make_boldmenghiasi hasil make_italicdekorator yang menghiasi hellofungsinya.

The @decoratorsintaks gula benar-benar hanya sintaksis; pengikut:

@decorator
def decorated_function():
    # ...

benar-benar dijalankan sebagai:

def decorated_function():
    # ...
decorated_function = decorator(decorated_function)

mengganti decorated_functionobjek asli dengan apa pun yang decorator()dikembalikan.

Dekorator bertumpuk mengulangi proses itu ke luar .

Jadi sampel Anda:

@make_bold
@make_italic
def hello():
  return "hello world"

dapat diperluas menjadi:

def hello():
  return "hello world"
hello = make_bold(make_italic(hello))

Saat Anda menelepon hello()sekarang, Anda memanggil objek yang dikembalikan oleh make_bold(), sungguh. make_bold()mengembalikan a lambdayang memanggil fungsi make_bolddibungkus, yang merupakan nilai kembalian make_italic(), yang juga merupakan lambda yang memanggil aslinya hello(). Memperluas semua panggilan ini yang Anda dapatkan:

hello() = lambda : "<b>" + fn() + "</b>" #  where fn() ->
    lambda : "<i>" + fn() + "</i>" # where fn() -> 
        return "hello world"

sehingga outputnya menjadi:

"<b>" + ("<i>" + ("hello world") + "</i>") + "</b>"
Martijn Pieters
sumber
Saya mengerti. Tetapi apakah ini berarti bahwa jika ada 2 pembungkus dalam kasus ini, IDE akan secara otomatis mendeteksi dan menggabungkan hasil pembungkus pertama? Karena saya pikir begitu @make_bold #make_bold = make_bold(hello) @make_italic #make_italic = make_italic (hello)? Saya tidak yakin jika berdasarkan ini, itu akan membungkus hasil pertama. Atau untuk kasus 2 pembungkus ini, IDE akan menggunakan make_bold(make_italic(hello))seperti yang Anda sebutkan, bukan yang saya bagikan?
Pemula
3
@Newbie: IDE Anda tidak melakukan apa pun di sini; itu adalah Python yang melakukan pembungkus. Saya tunjukkan dalam sampel terakhir saya yang make_bold()membungkus output make_italic(), yang digunakan untuk membungkus hello, jadi setara dengan make_bold(make_italic(hello)).
Martijn Pieters
Bisakah Anda memberikan versi kode ini tanpa menggunakan lambda? Saya telah mencoba .format tetapi tidak berhasil. Dan mengapa lambda digunakan dalam contoh ini? Saya mencoba memahami lambda dan cara kerjanya dalam contoh ini tetapi masih mengalami masalah. Saya mengerti bahwa lambda seperti fungsi satu baris yang dapat diteruskan dengan mudah dibandingkan dengan norma fungsi def?
Pemula
def inner: return "<b>" + fn() + "</b>", maka return innerakan menjadi versi fungsi 'biasa'; bukan perbedaan yang besar.
Martijn Pieters
Saya selalu bingung soal order. "... dekorator akan diterapkan mulai dengan yang paling dekat dengan pernyataan" def "" Saya menyebutnya "dalam-luar". Saya pikir Martijn menyebutnya "ke luar". Ini berarti make_italic dekorator dijalankan sebelum make_bold dekorator , karena make_italicpaling dekat dengan def. Namun, saya lupa bahwa urutan eksekusi kode yang didekorasi : yang make_bold didekorasi (yaitu lambda tebal) dijalankan terlebih dahulu, diikuti oleh lambda yang make_italic didekorasi (yaitu lambda miring).
The Red Pea