Fungsi panggilan python dari string dengan argumen

Sebelum saya mulai menulis kode Python, saya menghabiskan sebagian besar waktu saya bekerja dengan PHP. Ada beberapa hal yang membuat PHP menjadi warga negara kelas satu untuk bahasa tersebut. Salah satunya adalah inklusi kelas dinamis, dan yang lainnya adalah kemampuan untuk memanggil fungsi secara dinamis

Python di sisi lain tidak membuat keduanya mudah. Ada alasan bagus. Baik kelas pemuatan otomatis maupun fungsi yang dijalankan secara dinamis condong ke sisi magis pemrograman. Kode yang menggunakan pemanggilan fungsi dinamis dapat menjadi sangat cepat membingungkan

Sederhana lebih baik daripada kompleks. Kompleks lebih baik daripada rumit. Jumlah keterbacaan

Apa itu Panggilan Fungsi Dinamis?

Anda mungkin bertanya-tanya apa yang saya maksud dengan "secara dinamis" memanggil suatu fungsi. Mari kita lihat contoh cepat di PHP. Bahkan jika Anda belum pernah melihat PHP, Anda harus dapat memahami apa yang sedang terjadi

function area( int $length, int $width ) {
    print( $length * $width );
}

$area_func_name = 'area';

// Method 1
call_user_func( $area_func_name, 4, 5 );
// Output: 20

// Method 2
$area_func_name( 3, 5 );
// Output: 15

Catatan. dalam contoh ini deklarasi tipe bersifat opsional, tetapi merupakan praktik yang baik

Dalam contoh ini, fungsi area()_ dapat dipanggil selama kita mengetahui nama fungsi i. e.

def area(length: int, width: int):
    print(length * width)

area_func = globals()["area"]

area_func(5, 5)
# Output: 25
_0. Selama kita mengetahui nama fungsi yang ingin kita panggil, kita dapat mengeksekusi fungsi tersebut dan bahkan memberikan argumen

Ini adalah ide di balik eksekusi fungsi dinamis. Fungsi yang akan dieksekusi tidak ditentukan saat kode ditulis. Itu ditentukan saat runtime

Melakukan ini dengan Python sedikit lebih rumit. Seperti yang kami katakan sebelumnya, itu tidak selalu buruk. Untuk melakukan ini dengan Python, Anda harus mengakses namespace global. Python membuat Anda melakukan ini secara eksplisit di mana PHP tersirat

def area(length: int, width: int):
    print(length * width)

area_func = globals()["area"]

area_func(5, 5)
# Output: 25
_

Dalam contoh ini kami menggunakan fungsi

def area(length: int, width: int):
    print(length * width)

area_func = globals()["area"]

area_func(5, 5)
# Output: 25
_1 untuk mengakses namespace global.
def area(length: int, width: int):
    print(length * width)

area_func = globals()["area"]

area_func(5, 5)
# Output: 25
1 mengembalikan kamus yang menyertakan
def area(length: int, width: int):
    print(length * width)

area_func = globals()["area"]

area_func(5, 5)
# Output: 25
0 sebagai kunci dan nilainya adalah referensi ke fungsi area(). Jika Anda tidak terbiasa dengan
def area(length: int, width: int):
    print(length * width)

area_func = globals()["area"]

area_func(5, 5)
# Output: 25
_1, perubahan sederhana pada kode kami dapat memberi Anda gambaran yang jelas tentang apa yang kami lakukan

def area(length: int, width: int):
    print(length * width)

print(area)
# Output: 

print(globals()["area"])
# Output: 

Anda dapat melihat bahwa

def area(length: int, width: int):
    print(length * width)

area_func = globals()["area"]

area_func(5, 5)
# Output: 25
0 dan
def area(length: int, width: int):
    print(length * width)

area_func = globals()["area"]

area_func(5, 5)
# Output: 25
7 menunjuk ke alamat yang sama di memori
def area(length: int, width: int):
    print(length * width)

area_func = globals()["area"]

area_func(5, 5)
# Output: 25
8. Ini berarti bahwa dalam pemanggilan lingkup saat ini
def area(length: int, width: int):
    print(length * width)

area_func = globals()["area"]

area_func(5, 5)
# Output: 25
9 dan
def area(length: int, width: int):
    print(length * width)

print(area)
# Output: 

print(globals()["area"])
# Output: 
0 akan menjalankan fungsi yang sama persis

Jangan Menyalahgunakan Global

Hanya karena Anda dapat melakukan sesuatu bukan berarti Anda harus melakukannya. Menggunakan

def area(length: int, width: int):
    print(length * width)

area_func = globals()["area"]

area_func(5, 5)
# Output: 25
_1 dengan cara ini sering dianggap tidak pythonic atau aman. Lebih aman menggunakan
def area(length: int, width: int):
    print(length * width)

print(area)
# Output: 

print(globals()["area"])
# Output: 
2 atau
def area(length: int, width: int):
    print(length * width)

print(area)
# Output: 

print(globals()["area"])
# Output: 
3 untuk mengakses lingkup lokal saja, tetapi tetap tidak ideal

Untungnya, objek dalam ruang lingkup dengan cara ini tidak seberbahaya yang seharusnya. Fungsi dan kelas bawaan Python tersedia di kunci

def area(length: int, width: int):
    print(length * width)

print(area)
# Output: 

print(globals()["area"])
# Output: 
4. Ini sangat penting, karena berarti menelepon
def area(length: int, width: int):
    print(length * width)

print(area)
# Output: 

print(globals()["area"])
# Output: 
5 akan menaikkan
def area(length: int, width: int):
    print(length * width)

print(area)
# Output: 

print(globals()["area"])
# Output: 
6. Anda sebenarnya perlu menelepon
def area(length: int, width: int):
    print(length * width)

print(area)
# Output: 

print(globals()["area"])
# Output: 
7

Jujur saja, fungsi dan kelas bawaan Python bukan satu-satunya objek yang dapat dieksploitasi dalam program apa pun

Cara yang lebih baik adalah menggunakan kelas untuk mengenkapsulasi metode yang ingin Anda sediakan untuk dieksekusi saat runtime. Anda dapat membuat setiap fungsi sebagai metode kelas. Ketika nama suatu fungsi diberikan, Anda dapat menguji untuk melihat apakah itu adalah atribut kelas dan dapat dipanggil

import math

class Rectangle:
    length: int
    width: int

    def __init__(self, length: int, width: int):
        self.length = length
        self.width = width

    def do_area(self):
        area = self.length * self.width
        print(f"The area of the rectangle is: {area}")

    def do_perimeter(self):
        perimeter = (self.length + self.width) * 2
        print(f"The perimeter of the rectangle is: {perimeter}")

    def do_diagonal(self):
        diagonal = math.sqrt(self.length ** 2 + self.width ** 2)
        print(f"The diagonal of the rectangle is: {diagonal}")

    def solve_for(self, name: str):
        do = f"do_{name}"
        if hasattr(self, do) and callable(func := getattr(self, do)):
            func()

rectangle = Rectangle(3, 5)

rectangle.solve_for('area')
rectangle.solve_for('perimeter')
rectangle.solve_for('diagonal')

# Output: 
# The area of the rectangle is: 15
# The perimeter of the rectangle is: 16
# The diagonal of the rectangle is: 5.830951894845301

Jika Anda melihat pernyataan

def area(length: int, width: int):
    print(length * width)

print(area)
# Output: 

print(globals()["area"])
# Output: 
_8 dalam metode
def area(length: int, width: int):
    print(length * width)

print(area)
# Output: 

print(globals()["area"])
# Output: 
9 dan menjadi sedikit bingung. Jangan khawatir. Saya menggunakan beberapa sintaks yang baru di Python 3. 8

Ekspresi penugasan

import math

class Rectangle:
    length: int
    width: int

    def __init__(self, length: int, width: int):
        self.length = length
        self.width = width

    def do_area(self):
        area = self.length * self.width
        print(f"The area of the rectangle is: {area}")

    def do_perimeter(self):
        perimeter = (self.length + self.width) * 2
        print(f"The perimeter of the rectangle is: {perimeter}")

    def do_diagonal(self):
        diagonal = math.sqrt(self.length ** 2 + self.width ** 2)
        print(f"The diagonal of the rectangle is: {diagonal}")

    def solve_for(self, name: str):
        do = f"do_{name}"
        if hasattr(self, do) and callable(func := getattr(self, do)):
            func()

rectangle = Rectangle(3, 5)

rectangle.solve_for('area')
rectangle.solve_for('perimeter')
rectangle.solve_for('diagonal')

# Output: 
# The area of the rectangle is: 15
# The perimeter of the rectangle is: 16
# The diagonal of the rectangle is: 5.830951894845301
0 memungkinkan Anda menetapkan nilai variabel dan mengevaluasi hasil ekspresi dalam satu baris

Fungsinya setara dengan yang berikut ini

def solve_for(self, name: str):
    do = f"get_{name}"
    if hasattr(self, do) and callable(getattr(self, do)):
        func = getattr(self, do)
        func()

Perbedaannya adalah

import math

class Rectangle:
    length: int
    width: int

    def __init__(self, length: int, width: int):
        self.length = length
        self.width = width

    def do_area(self):
        area = self.length * self.width
        print(f"The area of the rectangle is: {area}")

    def do_perimeter(self):
        perimeter = (self.length + self.width) * 2
        print(f"The perimeter of the rectangle is: {perimeter}")

    def do_diagonal(self):
        diagonal = math.sqrt(self.length ** 2 + self.width ** 2)
        print(f"The diagonal of the rectangle is: {diagonal}")

    def solve_for(self, name: str):
        do = f"do_{name}"
        if hasattr(self, do) and callable(func := getattr(self, do)):
            func()

rectangle = Rectangle(3, 5)

rectangle.solve_for('area')
rectangle.solve_for('perimeter')
rectangle.solve_for('diagonal')

# Output: 
# The area of the rectangle is: 15
# The perimeter of the rectangle is: 16
# The diagonal of the rectangle is: 5.830951894845301
_1 tidak perlu dievaluasi dua kali

Mari kita kembali ke contoh kita

Metode

def area(length: int, width: int):
    print(length * width)

print(area)
# Output: 

print(globals()["area"])
# Output: 
_9 benar-benar melakukan sebagian besar pekerjaan berat di sini. Ini pertama-tama mengambil
import math

class Rectangle:
    length: int
    width: int

    def __init__(self, length: int, width: int):
        self.length = length
        self.width = width

    def do_area(self):
        area = self.length * self.width
        print(f"The area of the rectangle is: {area}")

    def do_perimeter(self):
        perimeter = (self.length + self.width) * 2
        print(f"The perimeter of the rectangle is: {perimeter}")

    def do_diagonal(self):
        diagonal = math.sqrt(self.length ** 2 + self.width ** 2)
        print(f"The diagonal of the rectangle is: {diagonal}")

    def solve_for(self, name: str):
        do = f"do_{name}"
        if hasattr(self, do) and callable(func := getattr(self, do)):
            func()

rectangle = Rectangle(3, 5)

rectangle.solve_for('area')
rectangle.solve_for('perimeter')
rectangle.solve_for('diagonal')

# Output: 
# The area of the rectangle is: 15
# The perimeter of the rectangle is: 16
# The diagonal of the rectangle is: 5.830951894845301
_3 dan menambahkan
import math

class Rectangle:
    length: int
    width: int

    def __init__(self, length: int, width: int):
        self.length = length
        self.width = width

    def do_area(self):
        area = self.length * self.width
        print(f"The area of the rectangle is: {area}")

    def do_perimeter(self):
        perimeter = (self.length + self.width) * 2
        print(f"The perimeter of the rectangle is: {perimeter}")

    def do_diagonal(self):
        diagonal = math.sqrt(self.length ** 2 + self.width ** 2)
        print(f"The diagonal of the rectangle is: {diagonal}")

    def solve_for(self, name: str):
        do = f"do_{name}"
        if hasattr(self, do) and callable(func := getattr(self, do)):
            func()

rectangle = Rectangle(3, 5)

rectangle.solve_for('area')
rectangle.solve_for('perimeter')
rectangle.solve_for('diagonal')

# Output: 
# The area of the rectangle is: 15
# The perimeter of the rectangle is: 16
# The diagonal of the rectangle is: 5.830951894845301
4 ke depan. Ini seringkali merupakan ide yang bagus. Kelas Anda kemungkinan akan memiliki beberapa metode dan atribut tambahan yang tidak ingin Anda tampilkan

Setelah nama fungsi ditentukan, kita dapat memeriksa untuk memastikan bahwa itu adalah atribut yang valid dan fungsi yang dapat kita panggil. Hal terakhir yang kita lakukan hanyalah memanggil fungsi

Argumen Fungsi Dinamis

Meneruskan argumen ke fungsi dinamis sangatlah mudah. Kami hanya dapat membuat

def area(length: int, width: int):
    print(length * width)

print(area)
# Output: 

print(globals()["area"])
# Output: 
_9 menerima
import math

class Rectangle:
    length: int
    width: int

    def __init__(self, length: int, width: int):
        self.length = length
        self.width = width

    def do_area(self):
        area = self.length * self.width
        print(f"The area of the rectangle is: {area}")

    def do_perimeter(self):
        perimeter = (self.length + self.width) * 2
        print(f"The perimeter of the rectangle is: {perimeter}")

    def do_diagonal(self):
        diagonal = math.sqrt(self.length ** 2 + self.width ** 2)
        print(f"The diagonal of the rectangle is: {diagonal}")

    def solve_for(self, name: str):
        do = f"do_{name}"
        if hasattr(self, do) and callable(func := getattr(self, do)):
            func()

rectangle = Rectangle(3, 5)

rectangle.solve_for('area')
rectangle.solve_for('perimeter')
rectangle.solve_for('diagonal')

# Output: 
# The area of the rectangle is: 15
# The perimeter of the rectangle is: 16
# The diagonal of the rectangle is: 5.830951894845301
6 dan
import math

class Rectangle:
    length: int
    width: int

    def __init__(self, length: int, width: int):
        self.length = length
        self.width = width

    def do_area(self):
        area = self.length * self.width
        print(f"The area of the rectangle is: {area}")

    def do_perimeter(self):
        perimeter = (self.length + self.width) * 2
        print(f"The perimeter of the rectangle is: {perimeter}")

    def do_diagonal(self):
        diagonal = math.sqrt(self.length ** 2 + self.width ** 2)
        print(f"The diagonal of the rectangle is: {diagonal}")

    def solve_for(self, name: str):
        do = f"do_{name}"
        if hasattr(self, do) and callable(func := getattr(self, do)):
            func()

rectangle = Rectangle(3, 5)

rectangle.solve_for('area')
rectangle.solve_for('perimeter')
rectangle.solve_for('diagonal')

# Output: 
# The area of the rectangle is: 15
# The perimeter of the rectangle is: 16
# The diagonal of the rectangle is: 5.830951894845301
7 lalu meneruskannya ke
import math

class Rectangle:
    length: int
    width: int

    def __init__(self, length: int, width: int):
        self.length = length
        self.width = width

    def do_area(self):
        area = self.length * self.width
        print(f"The area of the rectangle is: {area}")

    def do_perimeter(self):
        perimeter = (self.length + self.width) * 2
        print(f"The perimeter of the rectangle is: {perimeter}")

    def do_diagonal(self):
        diagonal = math.sqrt(self.length ** 2 + self.width ** 2)
        print(f"The diagonal of the rectangle is: {diagonal}")

    def solve_for(self, name: str):
        do = f"do_{name}"
        if hasattr(self, do) and callable(func := getattr(self, do)):
            func()

rectangle = Rectangle(3, 5)

rectangle.solve_for('area')
rectangle.solve_for('perimeter')
rectangle.solve_for('diagonal')

# Output: 
# The area of the rectangle is: 15
# The perimeter of the rectangle is: 16
# The diagonal of the rectangle is: 5.830951894845301
8

def solve_for(self, name: str, *args, **kwargs):
    do = f"get_{name}"
    if hasattr(self, do) and callable(func := getattr(self, do)):
        func(*args, **kwargs)

Tentu saja, Anda perlu menangani argumen dalam fungsi yang akan dipanggil. Saat ini, tidak ada metode

import math

class Rectangle:
    length: int
    width: int

    def __init__(self, length: int, width: int):
        self.length = length
        self.width = width

    def do_area(self):
        area = self.length * self.width
        print(f"The area of the rectangle is: {area}")

    def do_perimeter(self):
        perimeter = (self.length + self.width) * 2
        print(f"The perimeter of the rectangle is: {perimeter}")

    def do_diagonal(self):
        diagonal = math.sqrt(self.length ** 2 + self.width ** 2)
        print(f"The diagonal of the rectangle is: {diagonal}")

    def solve_for(self, name: str):
        do = f"do_{name}"
        if hasattr(self, do) and callable(func := getattr(self, do)):
            func()

rectangle = Rectangle(3, 5)

rectangle.solve_for('area')
rectangle.solve_for('perimeter')
rectangle.solve_for('diagonal')

# Output: 
# The area of the rectangle is: 15
# The perimeter of the rectangle is: 16
# The diagonal of the rectangle is: 5.830951894845301
9 dalam contoh kami yang menerima argumen

Salah satu kerumitan menggunakan argumen dengan eksekusi fungsi dinamis adalah kebutuhan setiap fungsi untuk menangani argumen yang sama. Ini mengharuskan Anda untuk membakukan argumen fungsi Anda

Penggunaan Fungsi Dinamis di Python

Dua kegunaan umum untuk eksekusi fungsi dinamis di PHP adalah callback dan hook. Namun, dalam Python tidak satu pun dari ini sangat umum

Saya pikir fokus Python untuk menjadi eksplisit membuat pengembang tidak bergerak menuju pemrograman meta dan magis. Python juga menyediakan dekorator yang dapat digunakan untuk mengaitkan atau membungkus fungsi secara eksplisit. Kedua hal ini membatasi penggunaan pemanggilan fungsi dinamis

Saya pikir contoh terbaik dari eksekusi fungsi dinamis di Python adalah validasi formulir di Django. Katakanlah kita memiliki bentuk seperti ini

from django import forms

class RegisterForm(forms.Form):
    username = forms.CharField()
    email = forms.EmailField()

Ketika Django memvalidasi dan mem-parsing nilai ke tipe data asli Python, ia akan memanggil fungsi untuk setiap bidang. Fungsinya adalah

def solve_for(self, name: str):
    do = f"get_{name}"
    if hasattr(self, do) and callable(getattr(self, do)):
        func = getattr(self, do)
        func()
0 di mana
def solve_for(self, name: str):
    do = f"get_{name}"
    if hasattr(self, do) and callable(getattr(self, do)):
        func = getattr(self, do)
        func()
1 adalah nama bidang

Dalam contoh ini kita bisa menambahkan sebuah metode untuk memastikan nilai name valid dan diformat sesuai keinginan kita

from django import forms

class RegisterForm(forms.Form):
    username = forms.CharField()
    email = forms.EmailField()

    def clean_username(self):
        data = self.cleaned_data['username']
        return data.lower()

Ini adalah contoh yang sangat sederhana. Kami hanya mengambil nilai apa pun yang diberikan di

def solve_for(self, name: str):
    do = f"get_{name}"
    if hasattr(self, do) and callable(getattr(self, do)):
        func = getattr(self, do)
        func()
2 dan menjadikannya huruf kecil.
def solve_for(self, name: str):
    do = f"get_{name}"
    if hasattr(self, do) and callable(getattr(self, do)):
        func = getattr(self, do)
        func()
3 dipanggil dengan metode
def solve_for(self, name: str):
    do = f"get_{name}"
    if hasattr(self, do) and callable(getattr(self, do)):
        func = getattr(self, do)
        func()
4 Django

Metode

def solve_for(self, name: str):
    do = f"get_{name}"
    if hasattr(self, do) and callable(getattr(self, do)):
        func = getattr(self, do)
        func()
_5 dari Django adalah contoh bagus tentang cara menjalankan fungsi secara dinamis

class BaseForm:
    ...
    def _clean_fields(self):
        for name, field in self.fields.items():
            # value_from_datadict() gets the data from the data dictionaries.
            # Each widget type knows how to retrieve its own data, because some
            # widgets split data over several HTML fields.
            if field.disabled:
                value = self.get_initial_for_field(field, name)
            else:
                value = field.widget.value_from_datadict(self.data, self.files, self.add_prefix(name))
            try:
                if isinstance(field, FileField):
                    initial = self.get_initial_for_field(field, name)
                    value = field.clean(value, initial)
                else:
                    value = field.clean(value)
                self.cleaned_data[name] = value
                if hasattr(self, 'clean_%s' % name):
                    value = getattr(self, 'clean_%s' % name)()
                    self.cleaned_data[name] = value
            except ValidationError as e:
                self.add_error(name, e)

Alasan ________ 24 ________5 ada bukan karena tidak mungkin melakukan ini dengan cara lain. Tetapi karena itu menyediakan antarmuka yang bersih untuk pengembang membangun formulir di Django

Saya pikir ini adalah contoh bagus mengapa Anda mungkin ingin menggunakan pola ini. Secara umum, saya akan merekomendasikan untuk menghindarinya, tetapi ada kalanya hal itu membuat perpustakaan atau API mudah digunakan oleh pengembang. Dalam kasus tersebut, jangan menghindar untuk melakukannya, tetapi pastikan Anda melakukannya dengan aman

Bagaimana Anda memanggil fungsi dari string dengan Python?

Fungsi Python umumnya dipanggil menggunakan namanya. Namun, Anda juga dapat menggunakan string untuk memanggil fungsi. Untuk itu, gunakan locals() dan globals() .

Bagaimana Anda memanggil fungsi dengan argumen di Python?

Cara Mendefinisikan dan Memanggil Fungsi Dasar dengan Python .
Ketikkan nama fungsi
Nama fungsi harus diikuti dengan tanda kurung. Jika ada argumen yang diperlukan, mereka harus diteruskan dalam tanda kurung. Jika fungsi tidak menerima argumen apa pun, Anda masih memerlukan tanda kurung

Bagaimana Anda memanggil fungsi dari string?

Ada dua metode untuk memanggil fungsi dari string yang disimpan dalam variabel. .
Menggunakan metode objek jendela
Menggunakan eval() metode

Bisakah suatu fungsi mengambil fungsi sebagai argumen Python?

Karena fungsi adalah objek, kita dapat meneruskannya sebagai argumen ke fungsi lain . Fungsi yang dapat menerima fungsi lain sebagai argumen juga disebut fungsi tingkat tinggi.