web-dev-qa-db-fra.com

Est-il possible de créer des objets anonymes en Python?

Je débogue certains Python qui prend, en entrée, une liste d'objets, chacun avec quelques attributs.

Je voudrais coder en dur certaines valeurs de test - disons, une liste de quatre objets dont l'attribut "foo" est défini sur un certain nombre.

Y a-t-il un moyen plus concis que cela?

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

Idéalement, j'aimerais juste pouvoir dire quelque chose comme:

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

(Évidemment, c'est une syntaxe inventée. Mais y a-t-il quelque chose de similaire qui fonctionne vraiment?)

Remarque: Cela ne sera jamais archivé. Il s'agit simplement d'un code de débogage jetable. Ne vous inquiétez donc pas de la lisibilité ou de la maintenabilité.

47
mike

J'aime la solution de Tetha, mais elle est inutilement complexe.

Voici quelque chose de plus simple:

>>> class MicroMock(object):
...     def __init__(self, **kwargs):
...         self.__dict__.update(kwargs)
...
>>> def print_foo(x):
...     print x.foo
...
>>> print_foo(MicroMock(foo=3))
3
43
DzinX

J'ai trouvé ceci: http://www.hydrogen18.com/blog/python-anonymous-objects.html , et dans mes tests limités, il semble que cela fonctionne:

>>> obj = type('',(object,),{"foo": 1})()
>>> obj.foo
1
40
Nerdmaster

Jetez un œil à ceci:


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))
16
Tetha

Si bref, un tel Python! O.o

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

Ensuite, vous pouvez utiliser Object comme constructeur d'objet générique:

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

EDIT: Eh bien, techniquement, cela crée un objet de classe, pas un objet objet. Mais vous pouvez le traiter comme un objet anonyme ou modifier la première ligne en ajoutant une paire de parenthèses pour créer immédiatement une instance:

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

Un autre hack évident:

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

print(foo1.x, foo2.y)

Mais pour votre cas d'utilisation exact, en appelant directement une fonction avec des objets anonymes, je ne connais pas de ligne moins verbeuse que

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

Moche, fait le boulot, mais pas vraiment.

5
vlad-ardelean

Vous pouvez peut-être utiliser namedtuple pour résoudre ce problème comme suit:

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

mock = Mock(foo=1)
mock.foo  // 1
4
Huachao

Depuis Python 3.3, il y a types.SimpleNamespace qui fait exactement ce que vous voulez:

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

C'est un peu verbeux, mais vous pouvez le nettoyer avec un alias:

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

Et maintenant, c'est en fait assez proche de la syntaxe fictive dans votre question.

4
W. Marshall

Non chic:

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))
4
Rabih Kodeih
anonymous_object = type('',(),{'name':'woody', 'age':'25'})()
anonymous_object.name
> 'woody'

Il y a un moyen cool mais difficile à comprendre. Il utilise type () crée une classe sans nom avec des paramètres init par défaut, puis l'initialise sans aucun paramètre et obtient l'objet anonyme.

1
woody

Voici comment je l'ai fait:

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'
0
Asaf Pinhassi