Passing new fields to an object

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
10 messages Options
Reply | Threaded
Open this post in threaded view
|

Passing new fields to an object

Paulo da Silva-3
I would like to do something like this:

class C:
        def __init__(self,**parms):
        ...

c=C(f1=1,f2=None)

I want to have, for the object
        self.f1=1
        self.f2=None

for an arbitrary number of parameters.

What is the best way to achieve this?

Thanks


gst
Reply | Threaded
Open this post in threaded view
|

Passing new fields to an object

gst
Le vendredi 12 juin 2015 11:53:24 UTC-4, Paulo da Silva a ?crit?:

> I would like to do something like this:
>
> class C:
> def __init__(self,**parms):
> ...
>
> c=C(f1=1,f2=None)
>
> I want to have, for the object
> self.f1=1
> self.f2=None
>
> for an arbitrary number of parameters.
>
> What is the best way to achieve this?
>
> Thanks

in the __init__, simply do:

    self.__dict__.update(**parms)

regards,

gst.

Reply | Threaded
Open this post in threaded view
|

Passing new fields to an object

Peter Otten
In reply to this post by Paulo da Silva-3
Paulo da Silva wrote:

> I would like to do something like this:
>
> class C:
> def __init__(self,**parms):
> ...
>
> c=C(f1=1,f2=None)
>
> I want to have, for the object
> self.f1=1
> self.f2=None
>
> for an arbitrary number of parameters.
>
> What is the best way to achieve this?

Use a dict ;)

While I'd recommend that you spell out the possible arguments here is one
way to not obey my advice:

>>> import types
>>> class C(types.SimpleNamespace):
...     pass
...
>>> c = C(f1=1, f2=None)
>>> c
C(f1=1, f2=None)

If you want to do it manually you can either use

for name, value in parms.items():
    setattr(self, name, value)

or the less general

self.__dict__.update(parms)

Again, if you don't know the names in advance the appropriate data structure
is a dict.


Reply | Threaded
Open this post in threaded view
|

Passing new fields to an object

Paulo da Silva-3
In reply to this post by gst
On 12-06-2015 17:17, gst wrote:
> Le vendredi 12 juin 2015 11:53:24 UTC-4, Paulo da Silva a ?crit :

> in the __init__, simply do:
>
>     self.__dict__.update(**parms)
>
> regards,
>
Ok. Thanks.



Reply | Threaded
Open this post in threaded view
|

Passing new fields to an object

Paulo da Silva-3
In reply to this post by Paulo da Silva-3
On 12-06-2015 17:17, Peter Otten wrote:
> Paulo da Silva wrote:
>
...

>
>>>> import types
>>>> class C(types.SimpleNamespace):
> ...     pass
> ...
>>>> c = C(f1=1, f2=None)
>>>> c
> C(f1=1, f2=None)
>

Thanks for all your explanations.
This solution works. Would you please detail a little on how it works?
Or just point me out some readings.
I am confused because types.SimpleNamespace seems to be a class!
>From docs ...:
class SimpleNamespace:
    def __init__(self, **kwargs):
        self.__dict__.update(kwargs)
    def __repr__(self):
        keys = sorted(self.__dict__)
        items = ("{}={!r}".format(k, self.__dict__[k]) for k in keys)
        return "{}({})".format(type(self).__name__, ", ".join(items))
    def __eq__(self, other):
        return self.__dict__ == other.__dict__


Reply | Threaded
Open this post in threaded view
|

Passing new fields to an object

Peter Otten
Paulo da Silva wrote:

> On 12-06-2015 17:17, Peter Otten wrote:
>> Paulo da Silva wrote:
>>
> ...
>
>>
>>>>> import types
>>>>> class C(types.SimpleNamespace):
>> ...     pass
>> ...
>>>>> c = C(f1=1, f2=None)
>>>>> c
>> C(f1=1, f2=None)
>>
>
> Thanks for all your explanations.
> This solution works. Would you please detail a little on how it works?
> Or just point me out some readings.
> I am confused because types.SimpleNamespace seems to be a class!

It *is* a class, and by making C a subclass of SimpleNamespace C inherits
the initialiser which does the actual work of updating the __dict__ of the C
instance.

> From docs ...:

> class SimpleNamespace:
>     def __init__(self, **kwargs):
>         self.__dict__.update(kwargs)

The actual implementation is written in C (the language used to implement
the CPython interpreter), but following the example in the docs you can make
your own SimpleNamespace...

>>> class MySimpleNamespace:
...     def __init__(self, **parms): self.__dict__.update(parms)
...
>>> m = MySimpleNamespace(a=1, b=2)
>>> m.a
1
>>> m.b
2

and when you subclass it the subclass inherits the behaviour:

>>> class C(MySimpleNamespace):
...     pass
...
>>> c = C(x=10, y=20)
>>> c.x
10
>>> c.y
20



Reply | Threaded
Open this post in threaded view
|

Passing new fields to an object

Steven D'Aprano-11
In reply to this post by Paulo da Silva-3
On Fri, 12 Jun 2015 16:53:08 +0100, Paulo da Silva wrote:

> I would like to do something like this:
>
> class C:
> def __init__(self,**parms):
> ...
>
> c=C(f1=1,f2=None)
>
> I want to have, for the object
> self.f1=1
> self.f2=None
>
> for an arbitrary number of parameters.
>
> What is the best way to achieve this?


Others have suggested that you update the instance dict with the keyword
parameters:

    self.__dict__.update(parms)


But I suggest that you should be very careful with this technique,
because it can lead to surprising problems and bugs in your code. If your
class has methods (and what sort of class doesn't have methods?), this
will override them and lead to mysterious failures in your code:

instance = C(a=1, b=2, method=3)
# much later
result = instance.method(args)  # will raise exception


You should use SimpleNamespace, as Peter suggests, but *not* subclass it.
If you subclass it and add methods:

class C(SimpleNamespace):
    def foo(self, arg):
        print("called foo")


then you risk overriding foo method, as above. If you don't add methods,
there is no need to subclass.

Instead, use composition: your class should *contain* a SimpleNamespace,
not *be* one:

class C:
    def __init__(self, **param):
        self.ns = SimpleNamespace(param)
    def __getattr__(self, attrname):
        return getattr(self.ns, attrname)
    def foo(self, arg):
        print("called foo")


instance = C(a=1, b=2, foo=3)
# later
instance.foo("x")  # prints "called foo"


The special method __getattr__ only runs if the attribute name is not
found in the usual way, so the method foo will continue to be found and
not be overridden by the param foo.



--
Steve

Reply | Threaded
Open this post in threaded view
|

Passing new fields to an object

Paulo da Silva-3
In reply to this post by Paulo da Silva-3
On 12-06-2015 20:12, Peter Otten wrote:
> Paulo da Silva wrote:
>
>> On 12-06-2015 17:17, Peter Otten wrote:
>>> Paulo da Silva wrote:
>>>
>> ...
...

> It *is* a class, and by making C a subclass of SimpleNamespace C inherits
> the initialiser which does the actual work of updating the __dict__ of the C
> instance.
>

OK, OK ...
Somehow I "changed" my mind to think SimpleNamespace as an argument of
__init__ and not as a subclass. I should have looked better before
responding.
Sorry.

Thank you very much.


Reply | Threaded
Open this post in threaded view
|

Passing new fields to an object

Paulo da Silva-3
In reply to this post by Steven D'Aprano-11
On 13-06-2015 02:25, Steven D'Aprano wrote:
> On Fri, 12 Jun 2015 16:53:08 +0100, Paulo da Silva wrote:
>
...

>
> You should use SimpleNamespace, as Peter suggests, but *not* subclass it.
> If you subclass it and add methods:
>
> class C(SimpleNamespace):
>     def foo(self, arg):
>         print("called foo")
>
>
> then you risk overriding foo method, as above. If you don't add methods,
> there is no need to subclass.
>
> Instead, use composition: your class should *contain* a SimpleNamespace,
> not *be* one:
>
> class C:
>     def __init__(self, **param):
>         self.ns = SimpleNamespace(param)
>     def __getattr__(self, attrname):
>         return getattr(self.ns, attrname)
>     def foo(self, arg):
>         print("called foo")
>
>
> instance = C(a=1, b=2, foo=3)
> # later
> instance.foo("x")  # prints "called foo"
>
>
> The special method __getattr__ only runs if the attribute name is not
> found in the usual way, so the method foo will continue to be found and
> not be overridden by the param foo.

Always learning!

Thanks a lot.



Reply | Threaded
Open this post in threaded view
|

Passing new fields to an object

Peter Otten
In reply to this post by Steven D'Aprano-11
Steven D'Aprano wrote:

> On Fri, 12 Jun 2015 16:53:08 +0100, Paulo da Silva wrote:
>
>> I would like to do something like this:
>>
>> class C:
>> def __init__(self,**parms):
>> ...
>>
>> c=C(f1=1,f2=None)
>>
>> I want to have, for the object
>> self.f1=1
>> self.f2=None
>>
>> for an arbitrary number of parameters.
>>
>> What is the best way to achieve this?
>
>
> Others have suggested that you update the instance dict with the keyword
> parameters:
>
>     self.__dict__.update(parms)
>
>
> But I suggest that you should be very careful with this technique,
> because it can lead to surprising problems and bugs in your code. If your
> class has methods (and what sort of class doesn't have methods?), this
> will override them and lead to mysterious failures in your code:
>
> instance = C(a=1, b=2, method=3)
> # much later
> result = instance.method(args)  # will raise exception
>
>
> You should use SimpleNamespace, as Peter suggests, but *not* subclass it.
> If you subclass it and add methods:
>
> class C(SimpleNamespace):
>     def foo(self, arg):
>         print("called foo")
>
>
> then you risk overriding foo method, as above. If you don't add methods,
> there is no need to subclass.

I sometimes do it anyway if only to get a meaningful class name.

> Instead, use composition: your class should *contain* a SimpleNamespace,
> not *be* one:
>
> class C:
>     def __init__(self, **param):
>         self.ns = SimpleNamespace(param)

**param

>     def __getattr__(self, attrname):
>         return getattr(self.ns, attrname)
>     def foo(self, arg):
>         print("called foo")
>
>
> instance = C(a=1, b=2, foo=3)
> # later
> instance.foo("x")  # prints "called foo"

>>> c = C(ns=42)
>>> assert c.ns == 42
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AssertionError

Yes, it's less likely, but now that the OP has two convenient ways to
produce a name clash I think it's time to repeat that there is one bullet-
proof approach: use a dict ;)

> The special method __getattr__ only runs if the attribute name is not
> found in the usual way, so the method foo will continue to be found and
> not be overridden by the param foo.