2015-04-12 14 views
6

Tôi biết, điều này là sai, nhưng có thể không? Tôi nghĩ một đối tượng được coi là có thể lặp lại khi phương thức .__iter__ của nó trả về một trình lặp? Vậy tại sao điều này không hiệu quả?Làm cho int có thể lặp lại với bị cấm

>>> from forbiddenfruit import curse 
>>> def __iter__(self): 
...  for i in range(self): 
...   yield i 
>>> curse(int, "__iter__", __iter__) 
>>> for x in 5: 
...  print x 
... 
Traceback (most recent call last): 
File "<stdin>", line 1, in <module> 
TypeError: 'int' object is not iterable 

intkhông dường như có một phương pháp __iter__ bây giờ:

>>> int(5).__iter__ 
<bound method int.__iter__ of 5> 
+1

Lưu ý rằng sẽ rõ ràng là wor k: 'list ((5) .__ iter __())' là '[0, 1, 2, 3, 4]' – jonrsharpe

+0

Xem xét báo cáo lỗi cho thư viện. –

+1

Thú vị. Tôi cũng có thể làm 'x = (5) .__ iter __()' và sau đó gọi 'next' trên nó, nó hoạt động như mong đợi. – L3viathan

Trả lời

5

Các tháo của một vòng lặp for là:

import dis 

dis.dis("for _ in _: pass") 
#>>> 1   0 SETUP_LOOP    14 (to 17) 
#>>>    3 LOAD_NAME    0 (_) 
#>>>    6 GET_ITER 
#>>>   >> 7 FOR_ITER     6 (to 16) 
#>>>    10 STORE_NAME    0 (_) 
#>>>    13 JUMP_ABSOLUTE   7 
#>>>   >> 16 POP_BLOCK 
#>>>   >> 17 LOAD_CONST    0 (None) 
#>>>    20 RETURN_VALUE 

Vì vậy, chúng tôi muốn các GET_ITER opcode.

TARGET(GET_ITER) { 
    /* before: [obj]; after [getiter(obj)] */ 
    PyObject *iterable = TOP(); 
    PyObject *iter = PyObject_GetIter(iterable); 
    Py_DECREF(iterable); 
    SET_TOP(iter); 
    if (iter == NULL) 
     goto error; 
    PREDICT(FOR_ITER); 
    DISPATCH(); 
} 

Trong đó sử dụng PyObject_GetIter:

PyObject * 
PyObject_GetIter(PyObject *o) 
{ 
    PyTypeObject *t = o->ob_type; 
    getiterfunc f = NULL; 
    f = t->tp_iter; 
    if (f == NULL) { 
     if (PySequence_Check(o)) 
      return PySeqIter_New(o); 
     return type_error("'%.200s' object is not iterable", o); 
    } 
    else { 
     PyObject *res = (*f)(o); 
     if (res != NULL && !PyIter_Check(res)) { 
      PyErr_Format(PyExc_TypeError, 
         "iter() returned non-iterator " 
         "of type '%.100s'", 
         res->ob_type->tp_name); 
      Py_DECREF(res); 
      res = NULL; 
     } 
     return res; 
    } 
} 

này đầu tiên kiểm tra t->tp_iter cho vô hiệu.

Bây giờ, đây là điều mà làm cho tất cả mọi thứ vào:

class X: 
    pass 

X.__iter__ = lambda x: iter(range(10)) 

list(X()) 
#>>> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 

from forbiddenfruit import curse 

class X: 
    pass 

curse(X, "__iter__", lambda x: iter(range(10))) 

list(X()) 
#>>> Traceback (most recent call last): 
#>>> File "", line 16, in <module> 
#>>> TypeError: 'X' object is not iterable 

Khi bạn thiết lập một thuộc tính trên một lớp học bình thường, nó gọi PyType_Type->setattro:

static int 
type_setattro(PyTypeObject *type, PyObject *name, PyObject *value) 
{ 
    if (!(type->tp_flags & Py_TPFLAGS_HEAPTYPE)) { 
     PyErr_Format(
      PyExc_TypeError, 
      "can't set attributes of built-in/extension type '%s'", 
      type->tp_name); 
     return -1; 
    } 
    if (PyObject_GenericSetAttr((PyObject *)type, name, value) < 0) 
     return -1; 
    return update_slot(type, name); 
} 

Xem update_slot? Điều đó xảy ra và cập nhật vị trí, do đó cuộc gọi tiếp theo tới GET_ITER sẽ trúng tp->tp_iter trên X. Tuy nhiên, forbiddenfruit bỏ qua quá trình này và chỉ cần nhập từ điển vào lớp học. Điều này có nghĩa rằng PyLong_Type giữ mặc định của nó:

PyTypeObject PyLong_Type = { 
    ... 
    0,           /* tp_iter */ 
    ... 
}; 

Vì vậy

if (f == NULL) 

được kích hoạt,

if (PySequence_Check(o)) 

thất bại (vì nó không phải là một chuỗi) và sau đó nó chỉ là

return type_error("'%.200s' object is not iterable", o); 
Các vấn đề liên quan