Chức năng gọi python theo tên với các đối số

Trước khi bắt đầu viết mã Python, tôi dành phần lớn thời gian để làm việc với PHP. Có một vài điều mà PHP làm cho ngôn ngữ trở thành công dân hạng nhất. Một trong số đó là bao gồm lớp động và thứ hai là khả năng gọi hàm động

Mặt khác, Python không làm cho một trong hai điều này trở nên dễ dàng. Có lý do chính đáng. Cả các lớp tự động tải và các chức năng thực thi động đều nghiêng về khía cạnh kỳ diệu của lập trình. Mã sử ​​dụng lệnh gọi hàm động có thể trở nên khó hiểu rất nhanh

Đơn giản là tốt hơn phức tạp. Phức tạp tốt hơn phức tạp. số lượng khả năng đọc

Gọi hàm động là gì?

Bạn có thể tự hỏi ý tôi là gì khi gọi một hàm "động". Hãy xem một ví dụ nhanh trong PHP. Ngay cả khi bạn chưa bao giờ nhìn vào PHP, bạn vẫn có thể hiểu điều gì đang xảy ra

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

Ghi chú. trong ví dụ này, các khai báo kiểu là tùy chọn, nhưng chúng là một cách thực hành tốt

Trong ví dụ này, hàm area[] có thể được gọi miễn là chúng ta biết tên của hàm i. e.

def area[length: int, width: int]:
    print[length * width]

area_func = globals[]["area"]

area_func[5, 5]
# Output: 25
0. Miễn là chúng ta biết tên của hàm mà chúng ta muốn gọi, chúng ta có thể thực thi hàm và thậm chí truyền vào các đối số

Đây là ý tưởng đằng sau việc thực thi chức năng động. Hàm sẽ được thực thi không được xác định khi mã được viết. Nó được xác định trong thời gian chạy

Làm điều này trong Python phức tạp hơn một chút. Như chúng tôi đã nói trước đó, điều đó không hẳn là xấu. Để làm điều này trong Python, bạn phải truy cập không gian tên chung. Python khiến bạn làm điều này một cách rõ ràng khi PHP ẩn

def area[length: int, width: int]:
    print[length * width]

area_func = globals[]["area"]

area_func[5, 5]
# Output: 25

Trong ví dụ này, chúng tôi sử dụng hàm

def area[length: int, width: int]:
    print[length * width]

area_func = globals[]["area"]

area_func[5, 5]
# Output: 25
1 để truy cập không gian tên chung.
def area[length: int, width: int]:
    print[length * width]

area_func = globals[]["area"]

area_func[5, 5]
# Output: 25
1 trả về một từ điển bao gồm
def area[length: int, width: int]:
    print[length * width]

area_func = globals[]["area"]

area_func[5, 5]
# Output: 25
0 làm khóa và giá trị là tham chiếu đến hàm area[]. Nếu bạn không quen thuộc với
def area[length: int, width: int]:
    print[length * width]

area_func = globals[]["area"]

area_func[5, 5]
# Output: 25
1, một thay đổi đơn giản đối với quy tắc của chúng tôi có thể cho bạn ý tưởng rõ ràng về những gì chúng tôi đang làm

def area[length: int, width: int]:
    print[length * width]

print[area]
# Output: 

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

Bạn có thể thấy rằng cả

def area[length: int, width: int]:
    print[length * width]

area_func = globals[]["area"]

area_func[5, 5]
# Output: 25
0 và
def area[length: int, width: int]:
    print[length * width]

area_func = globals[]["area"]

area_func[5, 5]
# Output: 25
7 đều trỏ đến cùng một địa chỉ trong bộ nhớ
def area[length: int, width: int]:
    print[length * width]

area_func = globals[]["area"]

area_func[5, 5]
# Output: 25
8. Điều này có nghĩa là trong phạm vi hiện tại, việc gọi
def area[length: int, width: int]:
    print[length * width]

area_func = globals[]["area"]

area_func[5, 5]
# Output: 25
9 và
def area[length: int, width: int]:
    print[length * width]

print[area]
# Output: 

print[globals[]["area"]]
# Output: 
0 sẽ chạy cùng một chức năng

Đừng lạm dụng Globals

Chỉ vì bạn có thể làm điều gì đó không có nghĩa là bạn nên. Sử dụng

def area[length: int, width: int]:
    print[length * width]

area_func = globals[]["area"]

area_func[5, 5]
# Output: 25
1 theo cách này thường bị phản đối vì không phải Pythonic và cũng không an toàn. Sẽ an toàn hơn khi sử dụng
def area[length: int, width: int]:
    print[length * width]

print[area]
# Output: 

print[globals[]["area"]]
# Output: 
2 hoặc
def area[length: int, width: int]:
    print[length * width]

print[area]
# Output: 

print[globals[]["area"]]
# Output: 
3 để chỉ truy cập phạm vi cục bộ, nhưng vẫn không lý tưởng

May mắn thay, các đối tượng trong phạm vi theo cách này không nguy hiểm như nó có thể. Các hàm và lớp tích hợp sẵn của Python có sẵn trong khóa

def area[length: int, width: int]:
    print[length * width]

print[area]
# Output: 

print[globals[]["area"]]
# Output: 
4. Điều này rất quan trọng, vì nó có nghĩa là gọi
def area[length: int, width: int]:
    print[length * width]

print[area]
# Output: 

print[globals[]["area"]]
# Output: 
5 sẽ tăng một
def area[length: int, width: int]:
    print[length * width]

print[area]
# Output: 

print[globals[]["area"]]
# Output: 
6. Bạn thực sự cần phải gọi
def area[length: int, width: int]:
    print[length * width]

print[area]
# Output: 

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

Thành thật mà nói, các hàm và lớp tích hợp sẵn của Python không phải là đối tượng duy nhất có thể khai thác trong bất kỳ chương trình nào

Cách tốt hơn là sử dụng một lớp để đóng gói các phương thức bạn muốn cung cấp để thực thi trong thời gian chạy. Bạn có thể tạo từng chức năng như một phương thức của lớp. Khi tên của hàm được cung cấp, bạn có thể kiểm tra xem đó có phải là thuộc tính của lớp và có thể gọi được không

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

Nếu bạn đang xem câu lệnh

def area[length: int, width: int]:
    print[length * width]

print[area]
# Output: 

print[globals[]["area"]]
# Output: 
8 trong phương pháp
def area[length: int, width: int]:
    print[length * width]

print[area]
# Output: 

print[globals[]["area"]]
# Output: 
9 và hơi bối rối. Đừng lo. Tôi đang sử dụng một số cú pháp mới trong Python 3. 8

Biểu thức gán

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 cho phép bạn đặt giá trị của một biến và đánh giá kết quả của một biểu thức trong một dòng

Hàm này tương đương như sau

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

Sự khác biệt là

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 không cần đánh giá hai lần

Hãy quay lại ví dụ của chúng ta

Phương pháp

def area[length: int, width: int]:
    print[length * width]

print[area]
# Output: 

print[globals[]["area"]]
# Output: 
9 thực sự đang thực hiện hầu hết các công việc nặng nhọc ở đây. Đầu tiên, nó lấy
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 và nối thêm
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 vào phía trước. Đây thường là một ý tưởng tốt. Lớp của bạn có thể sẽ có thêm một vài phương thức và thuộc tính mà bạn không muốn để lộ

Khi tên của hàm đã được xác định, chúng ta có thể kiểm tra để đảm bảo rằng đó là một thuộc tính hợp lệ và một hàm mà chúng ta có thể gọi. Điều cuối cùng chúng ta làm chỉ đơn giản là gọi hàm

Đối số chức năng động

Truyền đối số cho hàm động rất đơn giản. Chúng tôi chỉ đơn giản là có thể làm cho

def area[length: int, width: int]:
    print[length * width]

print[area]
# Output: 

print[globals[]["area"]]
# Output: 
9 chấp nhận
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 và
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 sau đó chuyển nó cho
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]

Tất nhiên, bạn sẽ cần xử lý các đối số trong hàm sẽ được gọi. Hiện tại, không có phương thức

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 nào trong ví dụ của chúng tôi chấp nhận đối số

Một trong những điều phức tạp của việc sử dụng các đối số khi thực thi hàm động là yêu cầu mỗi hàm xử lý các đối số giống nhau. Điều này yêu cầu bạn chuẩn hóa các đối số chức năng của mình

Sử dụng hàm động trong Python

Hai trong số những cách sử dụng phổ biến để thực thi hàm động trong PHP là gọi lại và hook. Tuy nhiên, trong Python cả hai cái này đều không phổ biến

Tôi nghĩ rằng việc Python tập trung vào tính rõ ràng sẽ ngăn các nhà phát triển chuyển sang lập trình meta và ma thuật. Python cũng cung cấp các trình trang trí có thể được sử dụng để móc hoặc bọc một hàm một cách rõ ràng. Hai điều này giới hạn việc sử dụng các lệnh gọi hàm động

Tôi nghĩ ví dụ tốt nhất về thực thi hàm động trong Python là xác thực mẫu trong Django. Hãy nói rằng chúng tôi đã có một hình thức như thế này

from django import forms

class RegisterForm[forms.Form]:
    username = forms.CharField[]
    email = forms.EmailField[]

Khi Django xác thực và phân tích các giá trị thành các kiểu dữ liệu Python gốc, nó sẽ gọi một hàm cho từng trường. Hàm này là

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 trong đó
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 là tên của trường

Trong ví dụ này, chúng ta có thể thêm một phương thức để đảm bảo giá trị của tên hợp lệ và được định dạng theo cách chúng ta muốn

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[]

Đây là một ví dụ rất đơn giản. Chúng tôi chỉ lấy bất kỳ giá trị nào được đưa ra trong

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 và đặt nó thành chữ thường.
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 được gọi bằng phương thức
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 của Django

Phương thức

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 từ Django là một ví dụ điển hình về cách thực thi động một hàm

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]

Lý do ________ 24 _______5 tồn tại không phải vì không thể làm điều này theo bất kỳ cách nào khác. Nhưng vì nó cung cấp giao diện rõ ràng cho các nhà phát triển xây dựng biểu mẫu trong Django

Tôi nghĩ rằng đây là một ví dụ tốt về lý do tại sao bạn có thể muốn sử dụng mẫu này. Nói chung, tôi khuyên bạn nên tránh nó, nhưng đôi khi nó làm cho thư viện hoặc API dễ dàng sử dụng cho các nhà phát triển. Trong những trường hợp đó, đừng ngại thực hiện, nhưng hãy đảm bảo rằng bạn thực hiện một cách an toàn

Làm cách nào chúng ta có thể truyền đối số trong lệnh gọi hàm trong Python?

Thông tin có thể được truyền vào hàm dưới dạng đối số. Các đối số được chỉ định sau tên hàm, bên trong dấu ngoặc đơn . Bạn có thể thêm bao nhiêu đối số tùy thích, chỉ cần phân tách chúng bằng dấu phẩy.

Làm thế nào bạn có thể gọi một chức năng với các đối số?

Lệnh gọi theo phương thức tham chiếu truyền đối số cho hàm sao chép địa chỉ của đối số vào tham số chính thức . Bên trong hàm, địa chỉ được sử dụng để truy cập đối số thực được sử dụng trong cuộc gọi. Nó có nghĩa là những thay đổi được thực hiện đối với tham số ảnh hưởng đến đối số đã truyền.

Làm cách nào để gọi hàm từ tên được lưu trong chuỗi bằng Python?

Bạn có thể sử dụng hàm getattr[] nếu bạn cần gọi một phương thức lớp bằng tên chuỗi. .
Sử dụng hàm globals[] để lấy từ điển chứa các biến toàn cục của phạm vi hiện tại
Sử dụng chuỗi làm khóa từ điển để lấy hàm
Gọi chức năng

Phương thức __ gọi __ là gì?

Phương thức __call__ là một phần của các phương thức tích hợp trong Python còn được gọi là phương thức dunder hoặc magic vì có hai tiền tố và hậu tố gạch dưới trong tên phương thức. Ý tưởng chính của phương thức __call__ là viết một lớp và gọi nó như một hàm . Bạn có thể gọi nó là đối tượng có thể gọi được.

Chủ Đề