Is it possible to create anonymous objects in Python?

Viewed 61317

I'm debugging some Python that takes, as input, a list of objects, each with some attributes.

I'd like to hard-code some test values -- let's say, a list of four objects whose "foo" attribute is set to some number.

Is there a more concise way than this?

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

Ideally, I'd just like to be able to say something like:

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

(Obviously, that is made-up syntax. But is there something similar that really works?)

Note: This will never be checked in. It's just some throwaway debug code. So don't worry about readability or maintainability.

13 Answers

I like Tetha's solution, but it's unnecessarily complex.

Here's something simpler:

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

Have a look at this:


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

There is a cool way but hard to understand. It use type() create a no-named class with default init params, then init it without any param and get the anonymous object.

I will use lambda

obj = lambda: None
obj.s = 'abb'
obj.i = 122

This is how I did it:

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'

If you are using Python 3.7 or above, you can use named tuples to enhance the created object with immutability, docstring, and handy tuple methods:

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+ is required to ensure keys and values are in the same order.

However, if the list of attributes is predefined, you can use namedtuple directly, as Huachao suggested, there's no need to define the PyObj function and you can use it in v2.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]

Plus, it looks more like the syntax you are looking for.

Yes, I very much missed the straightforward anonymous objects in JavaScript, particularly in function return values, where you can just say

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

You can use a dictionary to get the same effect, but all those square brackets and single quotes look muddly. So I now do the following, which works:

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

It would feel nicer to have a global class RV, and instantiate it to get the same effect, but this way the function has no external dependencies.

Related