Tôi rất ngạc nhiên khi biết rằng các hàm và phương thức có thể được gán thuộc tính, tương tự như cách người ta có thể gán thuộc tính cho lớp, thể hiện của lớp và hầu hết các đối tượng khác
def do_thing[]:
return
do_thing.whatever = "hi"
print[do_thing.whatever]
#> hi
Cái gì?Vì lý do gì là điều này thậm chí có thể?
bạn có thể đang nghĩ
“Tất nhiên điều này hiệu quả. Chức năng là đối tượng. Tất cả mọi thứ là một đối tượng. Và do đó, điều hợp lý là các hàm có các thuộc tính có thể gán. Các lớp là các đối tượng. Các lớp có các thuộc tính có thể gán. Chức năng cũng là đối tượng. Do đó các chức năng cũng phải có các thuộc tính có thể gán. ”
Chà, chỉ vì một thứ gì đó là một đối tượng không có nghĩa nó là một thuộc tính được gán miễn phí cho tất cả. Trên thực tế, Python rõ ràng cấm gán thuộc tính trong một vài trường hợp phổ biến
Ví dụ: bạn không thể gán thuộc tính cho các chức năng tích hợp
print.some_data = "foo"
#> AttributeError: 'builtin_function_or_method' object has no attribute 'some_data'
Và bạn không thể gán thuộc tính cho các loại tích hợp
var = "stringy string"
var.some_data = "foo"
#> AttributeError: 'str' object has no attribute 'some_data'
Và vâng, cả hai đều là đối tượng
isinstance[print, object]
#> True
isinstance[var, object]
#> True
Đáng ngạc nhiên là Python cho phép gán thuộc tính cho các hàm và phương thức [không tích hợp sẵn]. Và bạn có thể đặt bất cứ thứ gì bạn muốn vào đó. Kiểm tra nó ra
def do_thing[]:
pass
do_thing.sneaky_func = eval
do_thing.sneaky_func["print['O_o']"]
#> O_o
Tại saoĐiều này không phải lúc nào cũng đúng. Các chức năng ban đầu không hỗ trợ gán thuộc tính. Nhưng mặc dù không hỗ trợ gán thuộc tính, các chức năng không bao giờ hoàn toàn không có thuộc tính. Ngay cả trong những ngày đầu của Python, các hàm có một số thuộc tính, trong đó có một số thuộc tính có thể ghi—cụ thể là
print.some_data = "foo"
#> AttributeError: 'builtin_function_or_method' object has no attribute 'some_data'
0Nếu bạn đã từng ghi lại mã của mình, thì có lẽ bạn đã quen thuộc với các chuỗi tài liệu. Docstrings là đường cú pháp cho phép một người viết tài liệu cho các mô-đun, lớp, phương thức và hàm bằng cách đặt một chuỗi ký tự nội tuyến ở đầu định nghĩa của đối tượng
def do_thing[]:
"""Here's some documentation"""
return
help[do_thing]
#> Help on function do_thing in module __main__:
#> do_thing[]
#> Here's some documentation
Chuỗi tài liệu của một hàm được lưu trữ trong thuộc tính
print.some_data = "foo"
#> AttributeError: 'builtin_function_or_method' object has no attribute 'some_data'
0 của nó________số 8_______Vậy chuỗi tài liệu phải làm gì với các thuộc tính chức năng?
Trong những ngày đầu của Python, các kỹ sư đã bắt đầu chiếm quyền điều khiển chuỗi tài liệu cho các mục đích không mong muốn—không sử dụng chuỗi tài liệu cho tài liệu mà thay vào đó để lưu trữ thông tin có thể truy cập theo chương trình khác về chức năng
Ví dụ, John Aycock đã viết một hệ thống phân tích cú pháp thông minh để quyết định có nên áp dụng một phương thức cho một chuỗi mục tiêu hay không bằng cách đánh giá xem chuỗi mục tiêu đó có khớp với một biểu thức chính quy có trong chuỗi tài liệu của phương thức hay không.
Để đối phó với việc khai thác chuỗi tài liệu, Barry Warsaw đã đề xuất, một phần mở rộng cho ngôn ngữ Python bổ sung hỗ trợ cho các thuộc tính phương thức/hàm. Nó đã được phê duyệt và triển khai trong Python 2. 1+
Khi nào thì thích hợp để sử dụng các thuộc tính chức năng?Theo tôi. hiếm khi
Nhiều Pythonistas có kinh nghiệm không biết rằng thậm chí có thể gán các thuộc tính chức năng. Nếu bạn đang làm công việc phát triển phần mềm hàng ngày, thì việc thiếu quen thuộc trong cộng đồng thường là dấu hiệu đỏ cho một tính năng ngôn ngữ
Điều đó đang được nói, có những lý do hợp lệ để người ta có thể lưu trữ siêu dữ liệu trên một chức năng
Các thuộc tính hàm rất hữu ích nếu bạn đang chuyển các hàm xung quanh và cần một cách để ghép thông tin vào các hàm đó để sử dụng sau này. Có lẽ bạn đang viết một hàm bậc cao hơn—một hàm lấy một hàm làm đối số. Bạn có thể muốn hàm bậc cao của mình xử lý hàm bậc thấp khác nhau tùy thuộc vào giá trị được lưu trữ trong một thuộc tính của hàm
def dog_feeder[food_type]:
print[f'feeding {food_type} to the dog']
dog_feeder.species = 'dog'
def cat_feeder[food_type]:
print[f'feeding {food_type} to the cat']
cat_feeder.species = 'cat'
def feed_pet[feed_func]:
# Higher order function that takes a feed function as argument
if feed_func.species == 'dog':
feed_func['dog food']
elif feed_func.species == 'cat':
pass
feed_pet[dog_feeder]
#> feeding dog food to the dog
feed_pet[cat_feeder]
#>
Tôi nên lưu ý rằng trong Python 3. 4+, trình trang trí
print.some_data = "foo"
#> AttributeError: 'builtin_function_or_method' object has no attribute 'some_data'
2 phục vụ tốt hơn trường hợp sử dụng trên. Tôi sẽ nói về print.some_data = "foo"
#> AttributeError: 'builtin_function_or_method' object has no attribute 'some_data'
3 trong một bài đăng trong tương lai. Nhưng quan điểm của tôi đứng. khi thực hiện lập trình bậc cao, các hàm mang dữ liệu về chính chúng có thể hữu íchvà tin nhắn của Paul Prescod tới danh sách gửi thư của python-dev mô tả một số trường hợp sử dụng khác
Ví dụ: người ta có thể muốn xác định các định dạng chuỗi tài liệu khác nhau để phù hợp với các IDE hoặc phương pháp tài liệu khác nhau
Tương tự như trình phân tích cú pháp của John Aycock. nếu bạn đang viết một lớp để duyệt cấu trúc dữ liệu và bạn muốn liên kết các loại nút cụ thể với các phương thức nhất định, thì danh sách các loại nút mà một phương thức hỗ trợ có thể được lưu trữ dưới dạng một thuộc tính của phương thức đó
Khi nào thì không thích hợp để sử dụng các thuộc tính chức năng?Nếu bạn cần một kho lưu trữ dữ liệu nhanh chóng và tin rằng ký hiệu dấu chấm đẹp hơn dict[key]
def settings[]:
return
settings.host = '0.0.0.0'
settings.port = 8080
app.run[host=settings.host, port=settings.port]
Nghiêm túc, đừng làm điều này. Nếu đoạn mã trên xuất hiện trên bàn của tôi để xem xét, tôi sẽ thẳng thắn vỗ lưng cho người kỹ sư đó vì đã thông minh, sau đó lịch sự yêu cầu họ bị tống vào trại cải tạo vì tội ác của mình
Nếu bạn cần lưu trữ dữ liệu, hãy sử dụng từ điển, lớp hoặc thậm chí là mô-đun. Bất cứ điều gì ngoại trừ một chức năng
Một lý do không phù hợp sâu sắc khác để tận dụng các thuộc tính hàm là bạn không hài lòng với hành vi có sẵn của lớp và muốn tạo các hàm có hành vi giống như lớp. Bạn có thể thực hiện điều đó với các thuộc tính hàm và một chút đệ quy thú vị
def Dog[]:
def bark[]:
print["bark bark"]
Dog.bark = bark
return Dog
fido = Dog[]
fido.bark[]
#> bark bark
Xin đừng làm điều này
Phần kết luậnTôi hy vọng bạn thích phần đi sâu vào các thuộc tính hàm Python này. Đây là một bài viết trong loạt bài về Python arcana mà tôi sẽ xuất bản trong vài tháng tới. Cần kiểm tra lại sớm để biết thêm