Một ví dụ tối thiểu sử dụng unittest.mock.Mock
từ thư viện tiêu chuẩn:
from unittest.mock import Mock
def example[]:
pass
example_mock = Mock[side_effect=example]
example_mock[]
#Pseudocode:
if example_mock.called:
print["foo bar"]
Đầu ra bảng điều khiển sau khi chạy tập lệnh:
foo bar
Cách tiếp cận này rất tốt vì nó không yêu cầu bạn sửa đổi chức năng example
, điều này rất hữu ích nếu bạn muốn thực hiện kiểm tra này trong một số mã thử nghiệm đơn vị, mà không cần sửa đổi mã nguồn [ví dụ: lưu trữ thuộc tính has_been_called
hoặc Bọc chức năng trong một bộ trang trí].
Giải trình
Như được mô tả trong tài liệu cho lớp unittest.mock.Mock
, đối số side_effect
đối với hàm tạo Mock[]
chỉ định "một hàm được gọi bất cứ khi nào giả được gọi là".
Thuộc tính
foo bar
0 chỉ định "một boolean đại diện cho dù đối tượng giả có được gọi là".Lớp
foo bar
1 có các thuộc tính khác mà bạn có thể thấy hữu ích, ví dụ:2: Một số nguyên cho bạn biết đối tượng giả đã được gọi là bao nhiêu lầnfoo bar
3: Đây là không có gì [nếu giả đã được gọi], hoặc các đối số mà giả được gọi lần cuối cùngfoo bar
4: Đây là danh sách tất cả các cuộc gọi được thực hiện cho đối tượng giả theo trình tự [vì vậy độ dài của danh sách là số lần nó được gọi]. Trước khi bất kỳ cuộc gọi nào được thực hiện, nó là một danh sách trốngfoo bar
Lớp
foo bar
1 cũng có các phương thức thuận tiện để đưa ra các câu lệnh ASPERST dựa trên số lần một đối tượng foo bar
1 được gọi là và những đối số nào được gọi với, ví dụ::7: khẳng định rằng giả được gọi là chính xác một lần và cuộc gọi đó là với các đối số được chỉ địnhfoo bar
Năm ngoái tôi đã gặp phải một tình huống mà tôi cần biết nếu một chức năng đã được gọi. Về cơ bản, chúng tôi đã cố gắng ngăn chặn việc tắt một vòng lặp sự kiện xoắn hai lần hoặc bắt đầu hai trong số đó. Dù sao, trong nghiên cứu của tôi, tôi tình cờ thấy một bài viết vui nhộn trên Stackoverflow cho thấy một vài cách để làm điều này.
Việc sử dụng đầu tiên thực tế là mọi thứ trong Python là một đối tượng, bao gồm cả chức năng. Hãy xem một ví dụ đơn giản:
def self_aware_function[a, b]: self_aware_function.has_been_called = True return a + b if __name__ == '__main__': self_aware_function.has_been_called = False for i in range[2]: if self_aware_function.has_been_called: print['function already called'] else: print['function not called'] self_aware_function[1, 2]
Trong ví dụ này, chúng tôi tạo một thuộc tính trên hàm mà chúng tôi đặt tên là has_been_called. Chúng tôi đặt nó thành True khi hàm được gọi. Khi bạn bắt đầu chương trình của mình, bạn sẽ muốn khởi tạo thuộc tính đó thành Sai, điều mà chúng tôi làm ở trên. Sau đó, chúng tôi sử dụng một vòng lặp để lặp hai lần. Lần đầu tiên thông qua nó sẽ kiểm tra xem chức năng đã được gọi. Vì nó không có, bạn sẽ thấy nó rơi vào tuyên bố khác. Bây giờ chúng tôi đã gọi hàm, lần thứ hai thông qua vòng lặp phần đầu tiên của câu lệnh IF thực thi.has_been_called. We set it to True when the function is called. When you start your program, you will want to initialize that attribute to False, which we do above. Then we use a for loop to loop twice. The first time through it will check if the function has been called. Since it hasn't, you will see it fall to the else statement. Now that we called the function, the second time through the loop the first part of the if statement executes.
Bài đăng Stackoverflow đó cũng đề cập đến một cách gọn gàng để sử dụng một trình trang trí để theo dõi các cuộc gọi chức năng. Đây là một ví dụ tôi đã viết:
import functools def calltracker[func]: @functools.wraps[func] def wrapper[*args]: wrapper.has_been_called = True return func[*args] wrapper.has_been_called = False return wrapper @calltracker def doubler[number]: return number * 2 if __name__ == '__main__': if not doubler.has_been_called: print["You haven't called this function yet"] doubler[2] if doubler.has_been_called: print['doubler has been called!']
Trong ví dụ này, tôi nhập functools và tạo một chất trang trí mà tôi đặt tên là CallTracker. Trong chức năng này, chúng tôi đã thiết lập cùng một thuộc tính mà chúng tôi đã làm trong ví dụ trước, nhưng trong trường hợp này, chúng tôi gắn nó vào trình bao bọc của chúng tôi [tức là người trang trí]. Sau đó, chúng tôi trang trí một chức năng và thử mã của chúng tôi. Câu lệnh IF đầu tiên kiểm tra xem chức năng đã được gọi chưa. Nó không, vì vậy chúng tôi tiếp tục và gọi nó. Sau đó, chúng tôi xác nhận rằng chức năng được gọi trong câu lệnh IF thứ hai của chúng tôi.functools and create a decorator that I dubbed calltracker. In this function, we set up the same attribute that we did in the previous example, but in this case we attach it to our wrapper [i.e. the decorator]. Then we decorate a function and give our code a try. The first if statement checks to see if the function has been called yet. It hasn't, so we go ahead and call it. Then we confirm that the function was called in our second if statement.
Gói lên
Mặc dù công cụ này chắc chắn rất hữu ích trong thời gian chạy, bạn cũng có thể thực hiện những việc tương tự bằng cách sử dụng mô -đun theo dõi của Python để theo dõi việc thực thi mã của bạn. Loại điều này cũng được thực hiện thông qua các công cụ bảo hiểm. Bạn cũng sẽ tìm thấy loại chức năng này trong các đối tượng giả của Python như một chế độ giả có thể biết khi nào nó được gọi.
Dù sao, bạn hy vọng sẽ thấy bài tập này thú vị như tôi đã làm. Mặc dù tôi đã biết rằng mọi thứ trong Python là một đối tượng, tôi đã không nghĩ đến việc sử dụng chức năng đó để thêm các thuộc tính vào các chức năng.