Một cuộc gọi có thể gọi được là gì?


310

Bây giờ thì rõ ràng siêu dữ liệu là gì , có một khái niệm liên quan mà tôi sử dụng mọi lúc mà không biết ý nghĩa thực sự của nó là gì.

Tôi cho rằng mọi người đều mắc lỗi một lần với dấu ngoặc đơn, dẫn đến ngoại lệ "đối tượng không thể gọi được". Hơn nữa, sử dụng __init____new__dẫn đến tự hỏi những gì đẫm máu này __call__có thể được sử dụng cho.

Bạn có thể cho tôi một số giải thích, bao gồm các ví dụ với phương pháp ma thuật?


Câu trả lời:


308

Một cuộc gọi là bất cứ điều gì có thể được gọi.

Các built-in callable (PyCallable_Check trong objects.c) kiểm tra nếu đối số là một trong hai:

  • một thể hiện của một lớp với một __call__phương thức hoặc
  • là loại có thành viên không null tp_call (c struct) biểu thị khả năng gọi khác (chẳng hạn như trong hàm, phương thức, v.v.)

Phương pháp được đặt tên __call__là ( theo tài liệu )

Được gọi khi thể hiện là '' được gọi '' là một hàm

Thí dụ

class Foo:
  def __call__(self):
    print 'called'

foo_instance = Foo()
foo_instance() #this is calling the __call__ method

6
Lưu ý rằng có thể gọi được dựng sẵn trong Python 3.0 để kiểm tra cuộc gọi
Eli Courtwright

13
@Eli: Hmm nghe có vẻ là một động thái rất tệ. callablethực sự cho bạn biết nếu một cái gì đó có thể gọi được hay không, trong khi kiểm tra cho __call__bạn không biết gì; Nếu một đối tượng ocung cấp __getattribute__hoặc __getattr__, hasattr(o, '__call__')có thể trả về True, nhưng ovẫn không thể gọi được vì Python bỏ qua __getattribute____getattr__cho các cuộc gọi. Cách thực sự duy nhất còn lại để kiểm tra nếu một cái gì đó có thể gọi được là EAFP.
L̲̳o̲̳̳n̲̳̳g̲̳̳p̲̳o̲̳̳k̲̳̳e̲̳̳

49
@Longpoke: Chỉ để ghi lại, vui lòng xem tài liệu về callable()Python 3.x : " Hàm này lần đầu tiên được xóa trong Python 3.0 và sau đó được đưa trở lại trong Python 3.2. ".
Tadeck

Có vẻ như trong python 3,8 chỉ có sự hiện diện của tp_callđược kiểm tra. Xem triển khai PyCallable_Check , đó là 3 dòng.
Michele Piccolini

84

Từ nguồn Python của object.c :

/* Test whether an object can be called */

int
PyCallable_Check(PyObject *x)
{
    if (x == NULL)
        return 0;
    if (PyInstance_Check(x)) {
        PyObject *call = PyObject_GetAttrString(x, "__call__");
        if (call == NULL) {
            PyErr_Clear();
            return 0;
        }
        /* Could test recursively but don't, for fear of endless
           recursion if some joker sets self.__call__ = self */
        Py_DECREF(call);
        return 1;
    }
    else {
        return x->ob_type->tp_call != NULL;
    }
}

Nó nói rằng:

  1. Nếu một đối tượng là một thể hiện của một số lớp thì nó có thể gọi được nếu nó có __call__thuộc tính.
  2. Khác đối tượng xlà callable iff x->ob_type->tp_call != NULL

Mô tả của tp_calllĩnh vực :

ternaryfunc tp_callMột con trỏ tùy chọn đến một hàm thực hiện việc gọi đối tượng. Đây phải là NULL nếu đối tượng không thể gọi được. Chữ ký giống như đối với PyObject_Call (). Lĩnh vực này được kế thừa bởi các kiểu con.

Bạn luôn có thể sử dụng hàm dựng sẵn callableđể xác định xem đối tượng đã cho có thể gọi được hay không; hoặc tốt hơn là chỉ cần gọi nó và bắt TypeErrorsau. callableđược xóa trong Python 3.0 và 3.1, sử dụng callable = lambda o: hasattr(o, '__call__')hoặc isinstance(o, collections.Callable).

Ví dụ, triển khai bộ đệm đơn giản:

class Cached:
    def __init__(self, function):
        self.function = function
        self.cache = {}

    def __call__(self, *args):
        try: return self.cache[args]
        except KeyError:
            ret = self.cache[args] = self.function(*args)
            return ret    

Sử dụng:

@Cached
def ack(x, y):
    return ack(x-1, ack(x, y-1)) if x*y else (x + y + 1) 

Ví dụ từ thư viện chuẩn, tệp site.py, định nghĩa tích hợp exit()quit()hàm:

class Quitter(object):
    def __init__(self, name):
        self.name = name
    def __repr__(self):
        return 'Use %s() or %s to exit' % (self.name, eof)
    def __call__(self, code=None):
        # Shells like IDLE catch the SystemExit, but listen when their
        # stdin wrapper is closed.
        try:
            sys.stdin.close()
        except:
            pass
        raise SystemExit(code)
__builtin__.quit = Quitter('quit')
__builtin__.exit = Quitter('exit')

10
Tôi tìm thấy ví dụ cho phương thức gọi rất sai vì nó trộn nó với một công thức cho bộ nhớ đệm và trang trí, không thêm gì cho sự hiểu biết về cuộc gọi
Florian Bösch

3
JF Sebastian, cũng chồng chất thêm các ví dụ bạn sao chép và dán từ một nơi khác không tối thiểu không giúp ích gì.
Florian Bösch

20
@JF Sebastian: Đó là BS rằng các ví dụ giống như cuộc sống tốt hơn. Tôi có thể chỉ cho bạn mã giống như cuộc sống sẽ khiến bạn khóc như một ví dụ. Các ví dụ đơn giản cũng hoạt động và chúng hoạt động tốt hơn để minh họa một cái gì đó vì chúng không làm sao lãng.
Florian Bösch

5
Bạn đang giải thích những gì có thể gọi được, nhưng bạn đã đưa ra một ví dụ về cách sử dụng các đối tượng có thể gọi được để xác định một trang trí. Tôi biết đó là một cách sử dụng điển hình của có thể gọi được nhưng điều này có thể gây nhầm lẫn cho những độc giả chỉ muốn biết những gì có thể gọi được và làm thế nào để sử dụng có thể gọi được . Tôi thích câu trả lời của @Florian Bösch.
KFL

2
@Kay: Tôi cũng thích câu trả lời của @Florian Bösch (ở dạng hiện tại). btw, một trang trí không phải là một cách sử dụng thông thường của một "có thể gọi được". Nhất "callables" điển hình là chức năng / phương pháp như def f(): ..., và lớp đối tượng như class C: ...ví dụ f, ''.strip, len, và Ctất cả đều có thể được gọi. Các trường hợp có một __call__()phương thức trong lớp của họ là tương đối hiếm.
jfs

37

Một cuộc gọi là một đối tượng cho phép bạn sử dụng dấu ngoặc đơn () và cuối cùng truyền một số tham số, giống như các hàm.

Mỗi khi bạn xác định một hàm python sẽ tạo một đối tượng có thể gọi được. Ví dụ: bạn có thể định nghĩa hàm func theo những cách này (giống nhau):

class a(object):
    def __call__(self, *args):
        print 'Hello'

func = a()

# or ... 
def func(*args):
    print 'Hello'

Bạn có thể sử dụng phương thức này thay vì các phương thức như doit hoặc chạy , tôi nghĩ rằng rõ ràng hơn để thấy obj () hơn obj.doit ()


37

Hãy để tôi giải thích ngược lại:

Xem xét điều này...

foo()

... như đường cú pháp cho:

foo.__call__()

Trường hợp foocó thể là bất kỳ đối tượng đáp ứng __call__. Khi tôi nói bất kỳ đối tượng nào, ý tôi là: các kiểu dựng sẵn, các lớp của riêng bạn và các thể hiện của chúng.

Trong trường hợp các loại tích hợp, khi bạn viết:

int('10')
unicode(10)

Về cơ bản bạn đang làm:

int.__call__('10')
unicode.__call__(10)

Đó cũng là lý do tại sao bạn không có foo = new inttrong Python: bạn chỉ cần làm cho đối tượng lớp trả về một thể hiện của nó __call__. Theo cách của tôi, Python giải quyết vấn đề này rất thanh lịch.


Về cơ bản bạn đang làm type(int).__call__(int, '10')type(unicode).__call__(unicode, '10'). Các câu lạc bộ luôn được gọi vào lớp của họ, không thông qua ví dụ. Và họ cũng không bao giờ đi qua siêu dữ liệu. Đối với hầu hết các trường hợp đó chỉ là một nitpick, nhưng đôi khi nó quan trọng.
Nhà vật lý điên

11

Một Callable là một đối tượng có __call__phương thức. Điều này có nghĩa là bạn có thể giả mạo các chức năng có thể gọi được hoặc thực hiện những thứ gọn gàng như Ứng dụng chức năng một phần trong đó bạn có chức năng và thêm một cái gì đó giúp tăng cường hoặc điền vào một số tham số, trả lại một cái gì đó có thể được gọi lần lượt (được gọi là Currying trong vòng tròn lập trình chức năng ).

Một số lỗi chính tả sẽ khiến trình thông dịch cố gắng gọi một cái gì đó mà bạn không có ý định, chẳng hạn như (ví dụ) một chuỗi. Điều này có thể tạo ra lỗi trong đó trình thông dịch cố gắng thực thi một ứng dụng không thể gọi được. Bạn có thể thấy điều này xảy ra trong một trình thông dịch python bằng cách làm một cái gì đó giống như bảng điểm bên dưới.

[nigel@k9 ~]$ python
Python 2.5 (r25:51908, Nov  6 2007, 15:55:44) 
[GCC 4.1.2 20070925 (Red Hat 4.1.2-27)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> 'aaa'()    # <== Here we attempt to call a string.
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'str' object is not callable
>>> 

9

__call__ làm cho bất kỳ đối tượng có thể được gọi là một chức năng.

Ví dụ này sẽ xuất 8:

class Adder(object):
  def __init__(self, val):
    self.val = val

  def __call__(self, val):
    return self.val + val

func = Adder(5)
print func(3)

7

Nói một cách đơn giản, một "có thể gọi được" là một cái gì đó có thể được gọi như một phương thức. Hàm tích hợp "callable ()" sẽ cho bạn biết liệu có thứ gì đó có thể gọi được hay không, như sẽ kiểm tra thuộc tính cuộc gọi . Các hàm có thể gọi được như các lớp, các thể hiện của lớp có thể gọi được. Xem thêm về điều này ở đâyở đây .


5

Trong Python, một cuộc gọi là một đối tượng mà kiểu này có một __call__phương thức:

>>> class Foo:
...  pass
... 
>>> class Bar(object):
...  pass
... 
>>> type(Foo).__call__(Foo)
<__main__.Foo instance at 0x711440>
>>> type(Bar).__call__(Bar)
<__main__.Bar object at 0x712110>
>>> def foo(bar):
...  return bar
... 
>>> type(foo).__call__(foo, 42)
42

Đơn giản vậy thôi :)

Điều này tất nhiên có thể bị quá tải:

>>> class Foo(object):
...  def __call__(self):
...   return 42
... 
>>> f = Foo()
>>> f()
42

3

Để kiểm tra chức năng hoặc phương thức của lớp có thể gọi được hay không có nghĩa là chúng ta có thể gọi hàm đó.

Class A:
    def __init__(self,val):
        self.val = val
    def bar(self):
        print "bar"

obj = A()      
callable(obj.bar)
True
callable(obj.__init___)
False
def foo(): return "s"
callable(foo)
True
callable(foo())
False

1
Bạn có chắc chắn callable(obj.__init___)không có dấu gạch dưới thêm (như trong AttributionError) không? Nếu không, bạn có chắc câu trả lời không Truedành cho câu hỏi đó không?
Nhà vật lý điên

2

Đó là thứ bạn có thể đặt "(args)" sau và mong đợi nó hoạt động. Một cuộc gọi thường là một phương thức hoặc một lớp. Các phương thức được gọi, các lớp được khởi tạo.


2

Callables thực hiện __call__phương thức đặc biệt để bất kỳ đối tượng nào có phương thức như vậy đều có thể gọi được.


Một cá thể mà bạn xác định __call__sẽ không thể gọi được nếu lớp không định nghĩa một phương thức như vậy.
Nhà vật lý điên

2

Callable là một loại hoặc lớp của "Hàm tích hợp hoặc Phương thức" với một cuộc gọi phương thức

>>> type(callable)
<class 'builtin_function_or_method'>
>>>

Ví dụ: print là một đối tượng có thể gọi được. Với chức năng tích hợp __call__ Khi bạn gọi hàm in , Python tạo một đối tượng có kiểu in và gọi phương thức của nó __call__ truyền tham số nếu có.

>>> type(print)
<class 'builtin_function_or_method'>
>>> print.__call__(10)
10
>>> print(10)
10
>>>

Cảm ơn bạn. Trân trọng, Maris


1
Một số thông tin ở đây là thẳng lên sai. Ví dụ: "Khi bạn gọi printhàm, Python tạo một đối tượng có kiểu in và gọi phương thức của nó __call__". Python không tạo đối tượng in. Nó chỉ gọi một cái gì đó tương đương với type(print).__call__(print, *args, **kwargs). Và câu đầu tiên không có nhiều ý nghĩa. Bạn có vẻ khó hiểu một đối tượng có thể gọi và chức năng "có thể gọi được".
Nhà vật lý điên
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.