Можно ли создавать анонимные объекты в Python?

Я отлаживаю некоторый Python, который принимает в качестве входных данных список объектов, каждый из которых имеет некоторые атрибуты.

Я хотел бы жестко закодировать некоторые тестовые значения, скажем, список из четырех объектов, чей атрибут "foo" установлен на некоторое число.

Есть ли более краткий способ, чем этот?

x1.foo = 1
x2.foo = 2
x3.foo = 3
x4.foo = 4
myfunc([x1, x2, x3, x4])

В идеале я просто хотел бы сказать что-то вроде:

myfunc([<foo=1>, <foo=2>, <foo=3>, <foo=4>])

(Очевидно, что это выдуманный синтаксис. Но есть ли что-то подобное, что действительно работает?)

Примечание. Это никогда не будет проверено. Это просто одноразовый отладочный код. Так что не беспокойтесь о читабельности или ремонтопригодности.


person mike    schedule 16.03.2009    source источник


Ответы (13)


Мне нравится решение Tetha, но оно излишне сложное.

Вот что-то проще:

>>> class MicroMock(object):
...     def __init__(self, **kwargs):
...         self.__dict__.update(kwargs)
...
>>> def print_foo(x):
...     print x.foo
...
>>> print_foo(MicroMock(foo=3))
3
person DzinX    schedule 16.03.2009
comment
Однако, если вы создаете их целую кучу, вы можете сэкономить время/память, используя данный словарь вместо создания нового и копирования всех записей. Впрочем, на самом деле это не имеет значения. - person Josh Lee; 17.03.2009
comment
ну, я могу быть субъективен, но я не вижу существенного упрощения. Да, вы используете init и обновляете, но все еще возитесь с dict. - person Tetha; 17.03.2009
comment
Вы правы, существенного упрощения нет. Он просто короче и не использует new, вот и все. - person DzinX; 17.03.2009
comment
Если вам нужен удобный синтаксис, _ является допустимым именем класса в python и на самом деле не более неразборчив, чем версия, указанная в вопросе. - person RoadieRich; 17.07.2013
comment
С недавней функцией Python вы можете использовать def mock(**kwargs): return type('',(),kwargs)() и сделать a = mock(foo=1) - person Taisuke Yamada; 18.08.2014

Я нашел это: http://www.hydrogen18.com/blog/python-anonymous-objects.html, и в моем ограниченном тестировании кажется, что это работает:

>>> obj = type('',(object,),{"foo": 1})()
>>> obj.foo
1
person Nerdmaster    schedule 06.04.2015
comment
Великолепно! И верно однострочный! - person Bostone; 28.02.2019
comment
Любить это. Это также работает с другими классами и инициализацией, например, вот анонимный объект, полученный из словаря Python и инициализированный двумя записями: type("", (dict,), {})({"a": 1, "b": 2}) - person Jens; 30.04.2020

Такой краткий, такой Питон! о.о

>>> Object = lambda **kwargs: type("Object", (), kwargs)

Затем вы можете использовать Object в качестве универсального конструктора объектов:

>>> person = Object(name = "Bernhard", gender = "male", age = 42)
>>> person.name
'Bernhard'
>>>

РЕДАКТИРОВАТЬ: Хорошо, технически это создает объект класса, а не объект объекта. Но вы можете рассматривать его как анонимный объект или изменить первую строку, добавив пару круглых скобок, чтобы немедленно создать экземпляр:

>>> Object = lambda **kwargs: type("Object", (), kwargs)()
person Danny Raufeisen    schedule 15.03.2017
comment
Это единственное не ужасное решение, которое я здесь видел. - person Felype; 11.09.2019
comment
Хороший. Должен быть PEP для добавления нового ключевого слова. Может быть, изменить имя с Object на Anon? - person Riaz Rizvi; 06.02.2020

Посмотри на это:


class MiniMock(object):
    def __new__(cls, **attrs):
        result = object.__new__(cls)
        result.__dict__ = attrs
        return result

def print_foo(x):
    print x.foo

print_foo(MiniMock(foo=3))
person Tetha    schedule 16.03.2009

Возможно, вы можете использовать namedtuple, чтобы решить эту проблему следующим образом:

from collections import namedtuple
Mock = namedtuple('Mock', ['foo'])

mock = Mock(foo=1)
mock.foo  // 1
person Huachao    schedule 29.03.2018

Еще один очевидный взлом:

class foo1: x=3; y='y'
class foo2: y=5; x=6

print(foo1.x, foo2.y)

Но для вашего точного варианта использования, вызывая функцию с анонимными объектами напрямую, я не знаю ни одной строчки, менее подробной, чем

myfunc(type('', (object,), {'foo': 3},), type('', (object,), {'foo': 4}))

Некрасиво, работает, но не совсем.

person vlad-ardelean    schedule 28.01.2016

Начиная с Python 3.3 существует types.SimpleNamespace, который делает именно то, что ты хочешь:

myfunc([types.SimpleNamespace(foo=1), types.SimpleNamespace(foo=2), types.SimpleNamespace(foo=3), types.SimpleNamespace(foo=4)])

Это немного многословно, но вы можете убрать это с помощью псевдонима:

_ = types.SimpleNamespace
myfunc([_(foo=1), _(foo=2), _(foo=3), _(foo=4)])

И теперь это на самом деле довольно близко к вымышленному синтаксису в вашем вопросе.

person W. Marshall    schedule 18.07.2017
comment
Спасибо @RoadieRich за предложение _ в качестве имени класса в предыдущем комментарии. - person W. Marshall; 19.07.2017
comment
Если использование _ вас беспокоит (мне это кажется немного неприглядным и непитоновским), и вы действительно не хотите печатать так много, вы также можете просто from types import SimpleNamespace as Nsp и использовать Nsp как _ в своем ответе. - person JJC; 23.08.2018
comment
@JJC, конечно! Стандарты кодирования, с которыми я сейчас работаю, не позволяют переименовывать при импорте, поэтому я склонен забывать, что это вообще возможно. - person W. Marshall; 11.10.2018

anonymous_object = type('',(),{'name':'woody', 'age':'25'})()
anonymous_object.name
> 'woody'

Есть крутой способ, но сложный для понимания. Он использует type() для создания безымянного класса с параметрами инициализации по умолчанию, затем инициализирует его без каких-либо параметров и получает анонимный объект.

person woody    schedule 30.08.2018
comment
Python должен поддерживать анонимные типы. Они очень удобны в C#. Это самое чистое решение из всех ответов. - person michael; 16.02.2020
comment
@michael Да, это одна из замечательных функций C#. C# имеет действительно приятный дизайн синтаксиса. - person woody; 06.05.2020

Не классный:

def mock(**attrs):
    r = lambda:0
    r.__dict__ = attrs
    return r 

def test(a, b, c, d):
    print a.foo, b.foo, c.foo, d.foo

test(*[mock(foo=i) for i in xrange(1,5)])
# or
test(mock(foo=1), mock(foo=2), mock(foo=3), mock(foo=4))
person Rabih Kodeih    schedule 17.07.2013

буду использовать лямбду

obj = lambda: None
obj.s = 'abb'
obj.i = 122
person Nam G VU    schedule 28.11.2020

Вот как я это сделал:

from mock import patch
import requests

class MockResponse:

    def __init__(self, text, status_code):
        self.text = text
        self.status_code = status_code


class TestSomething(unittest.TestCase):

    @patch('requests.get',return_value=MockResponse('the result',200))
    def test_some_request(self, *args, **kwargs):
        response = requests.get('some.url.com')
        assert response.text=='the result'
        assert response.status_code=='200'
person Asaf Pinhassi    schedule 16.04.2019

Если вы используете Python 3.7 или выше, вы можете использовать именованные кортежи, чтобы улучшить созданный объект с помощью неизменяемости, строки документации и удобных методов кортежа:

from collections import namedtuple

PyObj = lambda **kwargs: namedtuple('PyObj', kwargs.keys())._make(kwargs.values())

o = PyObj(foo = 1)
print(o)
# prints: PyObj(foo=1)
o.foo
# returns: 1
o.foo = 0
# exception:
# Traceback (most recent call last):
#   File "<stdin>", line 1, in <module>
# AttributeError: can't set attribute

print(PyObj(foo = 1, bar = 'baz'))
# prints: PyObj(foo=1, bar='baz')

Требуется Python 3.7+, чтобы ключи и значения находились в одном порядке.

Однако, если список атрибутов предопределен, вы можете использовать namedtuple напрямую, как было предложено Huachao, нет необходимости определять PyObj, и вы можете использовать ее в версии 2.7.

from collections import namedtuple
foo = namedtuple('Foo', 'foo')

myfunc = lambda l: [x.foo * 10 for x in l]
myfunc([foo(1), foo(2), foo(3), foo(4)])
# returns [10, 20, 30, 40]

Кроме того, это больше похоже на синтаксис, который вы ищете.

person Glauco Aquino    schedule 15.01.2021

Да, мне очень не хватало простых анонимных объектов в JavaScript, особенно в возвращаемых значениях функций, где можно просто сказать

function george() { 
    return {fred:6, jim:9}; 
}
x = george();
y = x.fred;

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

def fred():
    class rv:
        x=0
    rv.y = 6
    return rv

def jim():
    class rv:
        x=0
    rv.y = 9
    return rv
a = fred()
b = jim()
print(a.y, b.y, id(a.y), id(b.y))

Было бы лучше иметь глобальный класс RV и создавать его экземпляры для получения того же эффекта, но таким образом функция не имеет внешних зависимостей.

person John White    schedule 30.01.2021