Как стать автором
Обновить

Комментарии 15

Я не понимаю поведения в питоне

1)Если по определению a = [1,2,3] и *a = 1,2,3 (пишу образно для понимания моего не понимания :) ), то в функции printScores(student, *scores) когда вызываем printScores(«Jonathan», 100, 95, 88, 92, 99) получается мы делам обратную операцию *, от всех «следующих аргументов» и тут возникает вопрос, почему не от всех аргументов, а только от «последних», а что если я хочу такую функцию printScores(student, *scores, *otherData)?
И вообще почему нельзя сделать так *a = [1,2,3], что бы получить a = [[1,2,3]]?

2)def printPetNames(owner, **pets) — тут двойная звездочка, в моём понимании по определению это двойная операция *, т.е. если **pets = dog=«Brock», fish=[«Larry», «Curly», «Moe»], turtle=«Shelldon», то *pets = {dog=«Brock», fish=[«Larry», «Curly», «Moe»], turtle=«Shelldon»]} а pets = [{dog=«Brock», fish=[«Larry», «Curly», «Moe»], turtle=«Shelldon»]}]

имхо оператор * имеет только похожее поведение на то что используется в функциях (*args и **kwargs)

1) Если ты напишешь print_scores(arg1, arg2, ..., argN, *scores), предполагается, что у тебя N аргументов обязательные, а остальные опциональные, и эти остальные соберутся в отдельный кортеж. То есть конструкция func(arg, *args1, *args2) невозможна, потому что непонятно, как поделить остальные переменные между args1 и args2. Если тебе хочется получить два списка аргументов, лучше явно запихни их в списки (fun(arg1=(1, 2, 3), arg2=(4, 5, 6)))


Писать *a = [1, 2, 3] нельзя, потому что * это операция распаковки, а не обращение к указателю как в C++. Даже если предположить, что такая конструкция имела бы место, это было бы сильно не pythonic way, потому что гораздо проще и понятнее то, что ты написал: a = [[1, 2, 3]].


2) Двойная звёздочка это не двойная распаковка. Это сборка именованных параметров в словарь и наоборот. Одинарная звёздочка на словарь вернёт тебе список ключей. Например,


def fun(*args):
    print(', '.join(args))

fun(*{'kek': 4, 'lol': 8, 'cheburek': 15})

напечатает на экране: kek, lol, cheburek. Логика «двойной оператор это то же самое, что два одинарных» не работает, потому что есть же разница между = и ==.


UPD: Коллега ниже отметил, что звезда собирает аргументы в кортеж, исправил оговорку.

В описании функции не допускается многократное использование позиционных и именованных аргументов, то есть такое описание функции, как printScores(student, *scores, *otherData), или же printScores(student, **kwargs, **other_kwargs) недопустимо — интерпретатор упадёт с ошибкой «invalid syntax». Оно и понятно — если более одного набора позиционных или именованных аргументов — нет возможности однозначно поделить аргументы между ними.

Позиционные аргументы приходят в виде кортежа (tuple), который как раз определяется звёздочкой, именованные аргументы приходят в виде словаря dict, и двойная звёздочка тут есть именно задание словаря. То есть при описании функции звёздочки это не операторы, выполняющие какое-либо действие, а именно описание параметров функции, так для printScores(student, *args, **kwargs) мы указываем, что внутри функции мы будем работать с кортежем args и словарём kwargs, по которым будут «разбросаны» все позиционные и именованные аргументы вызова функции. И чтобы не было путаницы, в вызове сначала указываются позиционные параметры (попадают в кортеж args с сохранением последовательности), а потом именованные, то есть вызов printScores('name', param='value', 42) недопустим.

При вызове printScores('name', [1,2,3]) в scores будет кортеж ([1,2,3]), ну или, если кортеж преобразовать в список, то будет список: list(scores) = [[1,2,3]]

Дело в том, что звёздочки — это не процедуры, а часть синтаксиса. Смысл звёздочек в определении процедуры и в выражении, вызывающем процедуру — разный.


*x в списке формальных аргументов — обозначает, что принимается произвольное число аргументов, которые в теле процедуры будут доступны в кортеже под именем x. (slurp)
*x в вызове процедуры — обозначает, что коллекция x распаковывается подставляется как отдельные аргументы. (splat)


Соответственно, если a = [1, 2, 3], то *a вне контекста вызова процедуры — это синтаксическая ошибка.

Возможно я не понял, что значит "вне контекста вызова процедуры", но позволю себе не согласиться, т.к. следующий код прекрасно работает:


first, *rest = 1, 2, 3, 4
print(rest)  
# [2, 3, 4]

Аналогично было бы и со списком:


first, *rest = [1, 2, 3, 4] 

Да, согласен, ещё распаковка при присваивании.
Но это тоже часть синтаксиса в том смысле, что *x самостоятельным выражением не является.

Отрывок очень не точной и поверхностной перепечатки документации — второе место в топе Хабра. Согласен что для совсем новичков в программировании статья может показаться полезной. Но блин на хабре я ожидаю более глубокие статьи. Может я не прав, может хабр для всех…

лично я ожидаю, что если статья написана для новичков (и даже для новичков-новичков), то профессионалы либо не станут читать или комментировать (особенно в духе выражения своего "Фи!") либо прокомментируют по существу — укажут на ошибку или добавят интересный случай/пример из практики

НЛО прилетело и опубликовало эту надпись здесь
Сокращаем статью до одной строки:
*args — array, **kwards — dictionary.

list*

А как реализованы листы внутри? — это масив(ы).

И? Это никоим образом не подтверждает ваши слова о том, что *args это массив.

Большое спасибо!
Отличная статья
Всё разложенно по полочкам
Теперь в голове наведён порядок по данному вопросу)
Удачи в дальнейшем написании интересного ;)

Зарегистрируйтесь на Хабре, чтобы оставить комментарий