Являются ли литералы объектов Pythonic?

В JavaScript есть литералы объектов, например.

var p = {
  name: "John Smith",
  age:  23
}

и .NET имеет анонимные типы, например.

var p = new { Name = "John Smith", Age = 23}; // C#

Нечто подобное можно эмулировать в Python, используя (ab) именованные аргументы:

class literal(object):
    def __init__(self, **kwargs):
        for (k,v) in kwargs.iteritems():
            self.__setattr__(k, v)
    def __repr__(self):
        return 'literal(%s)' % ', '.join('%s = %r' % i for i in sorted(self.__dict__.iteritems()))
    def __str__(self):
        return repr(self)

Использование:

p = literal(name = "John Smith", age = 23)
print p       # prints: literal(age = 23, name = 'John Smith')
print p.name  # prints: John Smith

Но считается ли такой код Pythonic?


person ShinNoNoir    schedule 26.07.2010    source источник
comment
питон, мне нравится это слово!   -  person Andrew Bullock    schedule 26.07.2010
comment
цикл for в вашем __init__ можно заменить на self.__dict__ = kw   -  person John La Rooy    schedule 27.07.2010


Ответы (8)


Рассматривали ли вы возможность использования именованного кортежа ?

Использование вашей нотации dict

>>> from collections import namedtuple
>>> L = namedtuple('literal', 'name age')(**{'name': 'John Smith', 'age': 23})

или аргументы ключевого слова

>>> L = namedtuple('literal', 'name age')(name='John Smith', age=23)
>>> L
literal(name='John Smith', age=23)
>>> L.name
'John Smith'
>>> L.age
23

Это поведение можно достаточно легко обернуть в функцию.

def literal(**kw):
    return namedtuple('literal', kw)(**kw)

лямбда-эквивалент будет

literal = lambda **kw: namedtuple('literal', kw)(**kw)

но лично я считаю глупым давать имена "анонимным" функциям

person John La Rooy    schedule 26.07.2010
comment
Да, я посмотрел на именованный кортеж, но не нарушает ли он принцип DRY? Создание объекта требует, чтобы вы дважды указывали имена каждого поля. Конечно, это можно обойти: literal = lambda **kwargs: namedtuple('literal', ' '.join(kwargs.keys()))(**kwargs); p = literal(name='John Smith', age=23) (Код не тестировался, на моей машине нет Py2.6) - person ShinNoNoir; 26.07.2010
comment
@ShinNoNoir, создание фабричной функции немного проще, чем ваше предложение, поскольку namedtuple довольно гибко подходит к своему второму параметру. - person John La Rooy; 27.07.2010
comment
@gnibbler, re: lambdas, я использовал их в своем комментарии из-за ограничений форматирования. Кроме того, давать имена лямбда-выражениям не так уж и глупо, если учесть лямбда-исчисление. Re: namedtuple, я не знал о его гибкости. - person ShinNoNoir; 27.07.2010
comment
Есть ли причина, по которой вы не можете просто сделать namedtuple('MyTypeName', [*someDict])(**someDict)? например, с someDict = {'a':10, 'b':20, 'c':30} - person jrh; 30.05.2019

Почему бы просто не использовать словарь?

p = {'name': 'John Smith', 'age': 23}

print p
print p['name']
print p['age']
person Wayne Werner    schedule 26.07.2010
comment
Вы также можете использовать функцию dict: p = dict(name='John Smith', age=23) - person Tomasz Wysocki; 26.07.2010
comment
Ну да, диктовки более-менее эквивалентны. Кроме того, предложение Томаша - хороший альтернативный способ их создания. Тем не менее, доступ к «полям» словаря не так удобен, как в моем примере кода, хотя это всего лишь небольшое синтаксическое неудобство. - person ShinNoNoir; 26.07.2010
comment
Хотя я согласен, я думаю, что есть философская разница в работе. Преобладающая философия Python заключается в том, что если вам нужен объект с членами .name и .age, вы должны явно создать этот объект со всеми его конструкторами и т. д. - person Wayne Werner; 26.07.2010
comment
да, конечно, но делать все это в интерактивном интерпретаторе во время тестирования/прототипирования/развлечения - это немного излишество. Вы знаете, что позже вы явно определите его, но прямо сейчас вы хотите перейти к сути... - person cji; 26.07.2010
comment
Правдивая история. Если это так, я бы поместил эту функцию в какой-нибудь файл инструментов и from tools import literals или что-то в этом роде всякий раз, когда я играл в интерпретаторе, потому что, когда это все, что вы делаете, не имеет большого значения, если это очень pythonic (многие люди используют 1 пробел для отступа - например, решительно не-pythonic [в реальном коде]). Кроме того, в любом случае неплохо иметь набор инструментов для настройки Python, которые вы используете довольно часто. - person Wayne Werner; 26.07.2010


Я не вижу ничего плохого в создании "анонимных" классов/экземпляров. Часто очень удобно создать его с помощью простого вызова функции в одной строке кода. Я лично использую что-то вроде этого:

def make_class( *args, **attributes ):
    """With fixed inability of using 'name' and 'bases' attributes ;)"""
    if len(args) == 2:
        name, bases = args
    elif len(args) == 1:
        name, bases = args[0], (object, )
    elif not args:
        name, bases = "AnonymousClass", (object, )
    return type( name, bases, attributes )

obj = make_class( something = "some value" )()
print obj.something

Для создания фиктивных объектов это работает просто отлично. Namedtuple подходит, но он неизменяем, что иногда может быть неудобно. И словарь... ну, словарь, но бывают ситуации, когда вам нужно передать что-то с определенным __getattr__ вместо __getitem__.

Я не знаю, pythonic это или нет, но иногда это ускоряет работу, и для меня это достаточно веская причина, чтобы использовать его (иногда).

person cji    schedule 26.07.2010
comment
Интересный подход, за исключением того, что make_class на самом деле создает объект. Также есть проблема, что у вас не может быть атрибута с именем name с вашим подходом. (Но я заметил, что у меня есть аналогичная проблема в моем классе literal... У меня не может быть атрибута с именем self). - person ShinNoNoir; 26.07.2010
comment
На самом деле это «объект класса» — он создан здесь: make_class(...)(). Я думаю? А насчет совпадений имен - Верно. - person cji; 26.07.2010

Я бы сказал, что реализованное вами решение выглядит довольно Pythonic; при этом types.SimpleNamespace (задокументировано здесь) уже включает эту функциональность:

from types import SimpleNamespace
p = SimpleNamespace(name = "John Smith", age = 23)
print(p)
person Maldus    schedule 08.03.2021

Из Python IAQ:

Начиная с Python 2.3 вы можете использовать синтаксис

dict(a=1, b=2, c=3, dee=4)

что достаточно хорошо, насколько я понимаю. До Python 2.3 я использовал однострочную функцию

def Dict(**dict): return dict
person Ken    schedule 26.07.2010

Я думаю, что литералы объектов имеют смысл в JavaScript по двум причинам:

  1. В JavaScript объекты — это единственный способ создать «вещь» со свойствами строкового индекса. В Python, как отмечено в другом ответе, это делает тип словаря.

  2. Объектная система JavaScript основана на прототипах. В JavaScript нет такого понятия, как класс (хотя он появится в будущей версии) — вместо классов у объектов есть объекты-прототипы. Таким образом, естественно создавать объект «из ничего» через литерал, потому что всем объектам требуется только встроенный корневой объект в качестве прототипа. В Python у каждого объекта есть класс — предполагается, что вы будете использовать объекты для вещей, в которых у вас будет несколько экземпляров, а не только для одноразовых.

Таким образом, нет, объектные литералы не Pythonic, а JavaScripthonic.

person Paul D. Waite    schedule 26.07.2010
comment
Нет, это газировка, которую часто смешивают с DOM Gin. - person Paul D. Waite; 26.07.2010
comment
Я не согласен - в JavaScript действительно есть только один способ создать диктоподобный объект, но к нему можно получить доступ более чем одним способом - через синтаксис obj.prop и obj["prop"] - это довольно хорошая идея, которую можно реализовать в Python, если кто-то хочет так (ответ Unode ниже). Кроме того, классы также являются объектами (в Python), поэтому их использование в качестве одноразовых контейнеров для атрибутов может быть полезно время от времени (при работе с API, которые хотят, чтобы __getattr__ было реализовано для чего-то переданного). - person cji; 26.07.2010
comment
@cji: положительные моменты относительно потенциальной полезности. Я понял, что «Pythonic» означает «использование встроенных функций языка перед реализацией пользовательских конструкций, вдохновленных другими языками», поэтому, не критикуя его, я поддержу идею, что такой класс, как literal, не совсем Pythonic. Ради интереса, есть ли у вас примеры API-интерфейсов, которые хотят передать что-то с реализованным __getattr__, где вы бы передали им одноразовую вещь? Я не очень разбираюсь в Python, поэтому пока не сталкивался с чем-то подобным. - person Paul D. Waite; 26.07.2010
comment
Один конкретный вариант использования, с которым я знаком, — это тестирование представлений Django. Часто возникает необходимость подделать некоторый объект для одного или двух конкретных представлений в тестовой среде. Я часто вижу там классы типа DummyObject или подобные — и это, конечно, хорошо, но иногда ты устаешь или спешишь и меньше ценишь каждый LOC. Ты знаешь, что завтра тебе придется это переписать, но сегодня вечером ты просто хочешь, чтобы эти чертовы тесты прошли и легли спать :) - person cji; 26.07.2010
comment
Ага, да, я немного поиграл в Django. Я буду следить за этим. - person Paul D. Waite; 27.07.2010
comment
Еще одна вещь, которую следует добавить, Python считает определяемые пользователем class и dict разными типами. Нет прямой поддержки доступа к значению с помощью dict.key. Тем не менее, Javascript поддерживает простой доступ с a.b точечной нотацией для поиска ключа b в словаре a, и фактически насколько я могу судить, class в Javascript - это просто синтаксический сахар над словарем. Это заставляет меня думать, что Python знает тип класса, и он должен быть предопределен, тогда как в Javascript вообще нет типов (только динамические объекты)... - person jrh; 30.05.2019
comment
... где динамический объект - это то, что реализовано словарем и не имеет метаданных; даже функции класса в Javascript могут быть просто записями в словаре, потому что функция может быть переменной (хотя, честно говоря, я не могу объяснить, как this реализовано в словарной системе JS; возможно, есть скрытый, сгенерированный первый self-подобный параметр, добавляемый к каждой функции при определении class). Это имеет смысл или я далеко здесь? - person jrh; 30.05.2019
comment
@jrh: в JavaScript есть типы, хотя они, вероятно, не очень важны в этом контексте. Я недостаточно разбираюсь в мельчайших подробностях JavaScript или Python, чтобы оценить, насколько точны ваши утверждения. - person Paul D. Waite; 30.05.2019

В большинстве случаев достаточно простого словаря.

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

class CustomDict(dict):
    def __getattr__(self, name):
        return self[name]

p = CustomDict(user='James', location='Earth')
print p.user
print p.location

Примечание. Имейте в виду, что, в отличие от namedtuples, поля не проверяются, и вы отвечаете за правильность своих аргументов. Такие аргументы, как p['def'] = 'something', допустимы внутри словаря, но вы не сможете получить к ним доступ через p.def.

person unode    schedule 26.07.2010