Is it possible to create anonymous objects in Python?

Question:

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.

Asked By: mike

||

Answers:

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))
Answered By: Tetha

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
Answered By: DzinX

Non classy:

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))
Answered By: Rabih Kodeih

I found this: http://www.hydrogen18.com/blog/python-anonymous-objects.html, and in my limited testing it seems like it works:

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

Another obvious hack:

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

print(foo1.x, foo2.y)

But for your exact usecase, calling a function with anonymous objects directly, I don’t know any one-liner less verbose than

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

Ugly, does the job, but not really.

Answered By: vlad-ardelean

Wow, so brief, such Python! O.o

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

Then you can use Object as a generic object constructor:

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

Now technically this creates a class object, not an object object. But you can treat it like an anonymous object or you modify the first line by appending a pair of parenthesis to create an immediate instance:

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

As of Python 3.3, there’s types.SimpleNamespace that does exactly what you want:

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

That’s a tad wordy, but you can clean it up with an alias:

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

And now that’s actually pretty close to the fictional syntax in your question.

Answered By: W. Marshall

Maybe you can use namedtuple to solve this as following:

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

mock = Mock(foo=1)
mock.foo  // 1
Answered By: Huachao
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.

Answered By: woody

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'
Answered By: Asaf Pinhassi

I will use lambda

obj = lambda: None
obj.s = 'abb'
obj.i = 122
Answered By: Nam G VU

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.

Answered By: Glauco Aquino

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.

Answered By: John White
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.