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é.
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
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
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))
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)()
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.
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
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.
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))
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.
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'