Вызов __new__ при создании подкласса кортежа

В Python при создании подкласса кортежа функция __new__ вызывается с self в качестве аргумента. Например, вот перефразированная версия класса PySpark Row:

class Row(tuple):
    def __new__(self, args):
        return tuple.__new__(self, args)

Но help(tuple) не показывает self аргумент для __new__:

  __new__(*args, **kwargs) from builtins.type
      Create and return a new object.  See help(type) for accurate signature.

и help(type) просто говорит то же самое:

__new__(*args, **kwargs)
      Create and return a new object.  See help(type) for accurate signature.

Итак, как self передается в __new__ в определении класса Row?

  • Это через *args?
  • Есть ли у __new__ какие-то тонкости, в которых его подпись может меняться в зависимости от контекста?
  • Или документация ошибочна?

Можно ли просмотреть источник tuple.__new__, чтобы я мог сам увидеть ответ?

Мой вопрос не дублирует этот, потому что в этом вопросе все обсуждения относятся к __new__ методам, которые явно имеют self или cls в качестве первого аргумент. Я пытаюсь понять

  1. Почему метод tuple.__new__ не имеет self или cls в качестве первого аргумента.
  2. Как я могу исследовать исходный код класса кортежей, чтобы лично увидеть, что происходит на самом деле.

person Paul    schedule 25.12.2015    source источник
comment
Сама документация, которую вы цитируете, говорит, что это не полная точная подпись.   -  person Daniel Roseman    schedule 25.12.2015
comment
Я это вижу, но help (type) не предоставляет дополнительной информации, просто та же подпись и тот же комментарий о точной подписи. Так что я все еще озадачен.   -  person Paul    schedule 25.12.2015
comment
Первый аргумент __new__ - это не экземпляр, а класс. Таким образом, его обычно называют cls, а не self. Под капотом tuple() == tuple.__new__(tuple) и tuple(iterable) == tuple.__new__(tuple, iterable).   -  person GingerPlusPlus    schedule 25.12.2015
comment
В документах говорится, что object.__new__(cls[, ...]) вызывается для создания нового экземпляра класса cls. __new__() - это статический метод (в особом регистре, поэтому вам не нужно объявлять его как таковой) , который принимает класс, экземпляр которого был запрошен, в качестве своего первого аргумента. (выделено мной) - возвращаемое значение станет self, переданным другим методам (в отличие, например, от __init__(), который не имеет возвращаемого значения).   -  person martineau    schedule 25.12.2015
comment
Как кортеж реализован в CPython?   -  person GingerPlusPlus    schedule 25.12.2015
comment
Собирая эти комментарии вместе, получается, что cls передается через *args, поскольку object.__new__ всегда должен передаваться cls. Спасибо @martineau и @GingerPlusPlus.   -  person Paul    schedule 25.12.2015
comment
Кстати, получать анонимные голоса против без объяснения причин обескураживает. Я прилагаю значительные усилия, чтобы задавать свои вопросы, следуя правилам сообщества. Я не читал все документы Python, но они очень длинные, и я не эксперт по Python, поэтому не всегда очевидно, как собрать всю информацию воедино. Негативные суждения и комментарии в отношении таких пользователей, как я, кажутся действительно нежелательными и противоречат цели сайта.   -  person Paul    schedule 25.12.2015
comment
См. Ответ на вопрос Сколько исследований ожидаются ли усилия от пользователей Stack Overflow?   -  person martineau    schedule 25.12.2015
comment
Не могу не согласиться с этим ответом. ИМХО тем, кто считает 99,99% вопросов постыдными, следует сосредоточиться на 0,01% вопросов, которые они находят интересными, а не тратить свой превосходный интеллект на голосование против более мелких смертных, борющихся с проблемами, которые они считают тривиальными. Но ответ, кажется, объясняет отношение к отрицательному голосу, так что +1 к вашему комментарию!   -  person Paul    schedule 25.12.2015


Ответы (1)


Правильная подпись tuple.__new__

Функции и типы, реализованные на C, часто невозможно проверить, и их сигнатуры всегда выглядят так.

Правильная подпись tuple.__new__:

__new__(cls[, sequence])

Например:

>>> tuple.__new__(tuple)
()
>>> tuple.__new__(tuple, [1, 2, 3])
(1, 2, 3)

Неудивительно, что это точно так же, как при вызове tuple(), за исключением того факта, что вам нужно повторить tuple дважды.


Первый аргумент __new__

Обратите внимание, что первым аргументом __new__ всегда является класс, а не экземпляр. Фактически, роль __new__ - создавать и возвращать новый экземпляр.

Специальный метод __new__ - это статический метод.

Я говорю это потому, что в вашем Row.__new__ я вижу self: хотя имя аргумента не важно (кроме случаев использования аргументов ключевого слова), помните, что self будет Row или подклассом Row, а не экземпляром. По общему соглашению первый аргумент следует называть cls вместо self.


Вернуться к вашим вопросам

Так как же self передается __new__ в определении класса Row?

Когда вы вызываете Row(...), Python автоматически вызывает Row.__new__(Row, ...).

  • Это через *args?

Вы можете написать свой Row.__new__ следующим образом:

class Row(tuple):
    def __new__(*args, **kwargs):
        return tuple.__new__(*args, **kwargs)

Это работает, и в этом нет ничего плохого. Это очень полезно, если вас не волнуют аргументы.

  • Есть ли у __new__ какие-то тонкости, в которых его подпись может меняться в зависимости от контекста?

Нет, единственная особенность __new__ в том, что это статический метод.

  • Или документация ошибочна?

Я бы сказал, что он неполный или неоднозначный.

  • Почему метод tuple.__new__ не имеет self или cls в качестве первого аргумента.

Он действительно есть, он просто не отображается на help(tuple.__new__), потому что часто эта информация не предоставляется функциями и методами, реализованными в C.

  • Как я могу исследовать исходный код класса tuple, чтобы лично убедиться, что происходит на самом деле.

Ищете файл Objects/tupleobject.c. В частности, вас интересует функция tuple_new():

static char *kwlist[] = {"sequence", 0};
/* ... */
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O:tuple", kwlist, &arg))

Здесь "|O:tuple" означает: функция называется "кортеж" и принимает один необязательный аргумент (| ограничивает необязательные аргументы, O обозначает объект Python). Необязательный аргумент может быть установлен через аргумент ключевого слова sequence.


О help(type)

Для справки вы просматривали документацию type.__new__, тогда как вам следовало остановиться на первых четырех строках help(type):

В случае __new__() правильной подписью является подпись type():

class type(object)
 |  type(object_or_name, bases, dict)
 |  type(object) -> the object's type
 |  type(name, bases, dict) -> a new type

Но это не актуально, так как tuple.__new__ имеет другую подпись.


Помните super()!

И последнее, но не менее важное: попробуйте использовать super() вместо прямого вызова tuple.__new__().

person Andrea Corbellini    schedule 25.12.2015
comment
Спасибо! Очень подробно, теперь все намного яснее. - person Paul; 25.12.2015
comment
что, если я хочу переопределить его параметр? \ n tuple2 унаследован от кортежа - person 喵喵喵; 19.05.2017
comment
Что, если я хочу переопределить его параметры? tuple2 наследуется от кортежа. Он действует как: tuple2 (2) == (0,0) 、 tuple2 (5) == (0,0,0,0,0), однако tuple .__ new__ принимает только итерацию, а не int. поэтому new выполняет всю оценку, и мы ничего не можем сделать в init - person 喵喵喵; 19.05.2017
comment
_ (: з 」∠) _не знаю, как сделать разрыв строки - person 喵喵喵; 19.05.2017
comment
@ 喵 喵 喵 вы можете определить свой собственный __new__ следующим образом: def __new__(cls, size): return super().__new__(cls, (0,) * size) - person Andrea Corbellini; 19.05.2017
comment
@AndreaCorbellini Если я напишу (0,) * размер в новый. Я присвоил ему значение по умолчанию. Поскольку кортеж неизменен, дальнейшее присвоение невозможно. - person 喵喵喵; 24.05.2017
comment
Я имею в виду, что если я изменяю неизменяемый подкласс, все назначения должны выполняться в 'новом'? - person 喵喵喵; 24.05.2017
comment
@ 喵 喵 喵 как вы сказали, кортежи неизменяемы. Если вам нужно что-то, что можно изменить, используйте список или массив или определите свой собственный класс: вы не обязаны наследовать от встроенных типов, на самом деле я бы отговорил это, если вы собираетесь радикально изменить семантику - person Andrea Corbellini; 24.05.2017