`super` trong một lớp con` gõ.NamedTuple` không thành công trong python 3.8


9

Tôi có mã làm việc trong Python 3.6 và bị lỗi trong Python 3.8. Nó dường như sôi sục để gọi supertrong lớp con của typing.NamedTuple, như dưới đây:

<ipython-input-2-fea20b0178f3> in <module>
----> 1 class Test(typing.NamedTuple):
      2     a: int
      3     b: float
      4     def __repr__(self):
      5         return super(object, self).__repr__()

RuntimeError: __class__ not set defining 'Test' as <class '__main__.Test'>. Was __classcell__ propagated to type.__new__?
In [3]: class Test(typing.NamedTuple): 
   ...:     a: int 
   ...:     b: float 
   ...:     #def __repr__(self): 
   ...:     #    return super(object, self).__repr__() 
   ...:                                                                         

>>> # works

Mục đích của super(object, self).__repr__cuộc gọi này là sử dụng tiêu chuẩn '<__main__.Test object at 0x7fa109953cf8>' __repr__thay vì in ra tất cả nội dung của các phần tử bộ (sẽ xảy ra theo mặc định). Có một số câu hỏi về superkết quả là lỗi tương tự nhưng chúng:

  1. Tham khảo phiên bản không có tham số super()
  2. Đã có lỗi trong Python 3.6 (nó hoạt động với tôi trước khi nâng cấp 3.6 -> 3.8)
  3. Tôi không hiểu làm thế nào để sửa lỗi này, vì nó không phải là siêu dữ liệu tùy chỉnh mà tôi có quyền kiểm soát mà là do stdlib cung cấp typing.NamedTuple.

Câu hỏi của tôi là làm thế nào tôi có thể sửa lỗi này trong khi duy trì khả năng tương thích ngược với Python 3.6 (nếu không tôi chỉ sử dụng @dataclasses.dataclassthay vì kế thừa từ typing.NamedTuple)?

Một câu hỏi phụ là làm thế nào điều này có thể thất bại tại thời điểm xác định cho rằng supercuộc gọi vi phạm nằm trong một phương thức thậm chí chưa được thực hiện. Ví dụ:

In [3]: class Test(typing.NamedTuple): 
   ...:     a: int 
   ...:     b: float 
   ...:     def __repr__(self): 
   ...:         return foo 

hoạt động (cho đến khi chúng tôi thực sự gọi __repr__) mặc dù foolà một tham chiếu không xác định. Là superma thuật trong vấn đề đó?


Khi tôi chạy mã của bạn trong Python 3.6, tôi nhận được một đại diện của superchính đối tượng, không phải là đại diện cho thể hiện của bạn Test.
chepner

Ngoài ra, có vẻ như bất cứ phép thuật biên dịch thời gian nào cho phép các đối số ngầm vẫn xảy ra để thực hiện một số xác nhận hợp lệ rõ ràng.
chepner

2
sử dụng __repr__ = object.__repr__trong định nghĩa lớp của bạn hoạt động với tôi trên Python3.6 & Python3.8
Azat Ibrakov

@chepner thực sự, bây giờ tôi bắt đầu bối rối về lý do tại sao nó hoạt động trước đây. Nhưng nó đã làm ...
Jatentaki

Vấn đề này được gây ra bởi siêu dữ liệu của typing.NamedTuple; typing.NamedTupleMeta, đó là làm một số shenanigans. super()yêu cầu __class__phải có sẵn tại thời gian biên dịch , rõ ràng không phải là trường hợp ở đây. Xem thêm: Cung cấp __classcell__ví dụ cho siêu dữ liệu Python 3.6
L3viathan

Câu trả lời:


2

Tôi đã hơi sai trong câu hỏi khác (mà tôi vừa cập nhật). Rõ ràng, hành vi này biểu hiện trong cả hai trường hợp super. Nhìn nhận lại, tôi nên đã thử nghiệm điều này.

Điều đang xảy ra ở đây là siêu dữ liệu NamedTupleMetathực sự không chuyển __classcell__qua type.__new__vì nó tạo ra một tên được đặt khi đang bay và trả lại nó. Trên thực tế, trong 3.6 và 3.7 của Python (nơi vẫn còn là một DeprecationWarning), các __classcell__rò rỉ vào từ điển lớp vì nó không bị xóa bởi NamedTupleMeta.__new__.

class Test(NamedTuple):
    a: int
    b: float
    def __repr__(self):
        return super().__repr__()

# isn't removed by NamedTupleMeta
Test.__classcell__
<cell at 0x7f956562f618: type object at 0x5629b8a2a708>

Sử dụng object.__repr__trực tiếp theo đề xuất của Azat thực hiện các mẹo.

Làm thế nào điều này có thể thất bại tại thời điểm định nghĩa

Cách tương tự như sau cũng thất bại:

class Foo(metaclass=1): pass

Nhiều kiểm tra được thực hiện trong khi lớp đang được xây dựng. Trong số đó, đang kiểm tra nếu metaclass đã thông qua việc __classcell__giao cho type_new.


Đây có phải là một lỗi trong stdlib không?
Jatentaki

@Jatentaki Việc rò rỉ __classcell__, tôi đã nghĩ như vậy. Hỗ trợ super, không biết, giống như cách nhiều kế thừa gần đây đã thay đổi đến bây giờ gây ra lỗi. Có thể vẫn còn giá trị để tạo ra một vấn đề, mặc dù.
Dimitris Fasarakis Hilliard

4

Thật không may, tôi không quen thuộc với thế hệ bên trong và lớp CPython để nói lý do tại sao nó thất bại, nhưng có vấn đề theo dõi lỗi CPython này dường như có liên quan và một số từ trong tài liệu Python

Chi tiết triển khai CPython: Trong CPython 3.6 trở lên, __class__ô được chuyển đến siêu dữ liệu dưới dạng một __classcell__mục trong không gian tên lớp. Nếu có, điều này phải được truyền tới lệnh type.__new__gọi để lớp được khởi tạo chính xác. Không làm như vậy sẽ dẫn đến một RuntimeErrorPython 3.8.

Vì vậy, có lẽ ở đâu đó trong quá trình namedtuplesáng tạo thực tế, chúng tôi có một cuộc gọi đến type.__new__mà không được __classcell__tuyên truyền, nhưng tôi không biết liệu đó có phải là trường hợp không.

Nhưng trường hợp cụ thể này dường như có thể giải quyết được bằng cách không sử dụng super()cuộc gọi với câu nói rõ ràng rằng "chúng ta cần phải có __repr__phương thức của objectlớp" như

class Test(typing.NamedTuple):
    a: int
    b: float
    __repr__ = object.__repr__
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.