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.
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))
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
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))
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
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.
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)()
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.
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
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.
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'
I will use lambda
obj = lambda: None
obj.s = 'abb'
obj.i = 122
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.
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.
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))
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
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))
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
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.
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)()
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.
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
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.
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'
I will use lambda
obj = lambda: None
obj.s = 'abb'
obj.i = 122
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.