1

I have class with a lot of fields that can be changed while my program is running, but when I create new object in my init I only can change some of them, I want to save those changes to JSON file and later be able to create new object with those variables. Is there any other way to do it other than making my init taking like 100 arguments?

In other words I would love it to be sth like that:

class MyClass:
    def __init__(self, q, w):
        self.q = q
        self.w = w
        self.e = 30
        self.r = 40
        
a = MyClass(10,20)
dct = {'q': 100, 'w': 200, 'e': 300, 'r': 400}
print('before:', tmp.q, tmp.w, tmp.e, tmp.r)
for i in dct:
    #do sth here
print('after:', tmp.q, tmp.w, tmp.e, tmp.r)
before: 10 20 30 40
after: 100 200 300 400
1

2 Answers 2

2

Here is how you can do that with keyword arguments:

class MyClass:
    def __init__(self, **q):
        self.__dict__.update(q)

a = MyClass(a=10, b=20, c=50, d=69)

print(a.a)
print(a.b)
print(a.c)
print(a.d)

Output:

10
20
50
69


With the dictionary:

class MyClass:
    def __init__(self, **q):
        self.__dict__.update(q)
        
dct = {'q': 100, 'w': 200, 'e': 300, 'r': 400}

a = MyClass(**dct)

print(a.q, a.w, a.e, a.r)

Output:

100 200 300 400
Sign up to request clarification or add additional context in comments.

3 Comments

I see, so if I want to create "fresh" instance I'd have to create it with dct that has all "starting" values, and if I create one that was already changed I use another one... that works, thank you :)
Arguable which is better and is contextual and subjective, but omitting ** in front of **q to avoid having to unpack a dict (and also allowing [[k,v],[k,v]...] form input) may make more sense, not only to avoid unpacking/packing, but also because logically MyClass is basically a wrapper class around the dict (or a level higher the JSON data), and it is only natural that the constructor take input in a form closer to an actual dict (and thus also the JSON) rather than just arbitrary named parameters, unnecessarily using the packing of arbitrary parameters and requirnig unpacking of input data.
(Important to note that this will not use getters setters if they are defined). And an alternative syntax available here using vars(): stackoverflow.com/questions/9204671/… I still don't really see any major problem with this implementation, but these things should all be noted.
1

Instead of manipulating the class instances __dict__, I'd recommend using the setattr method.

class MyClass:
    def __init__(self, new_vals):
        for k, v in new_vals.items():
            setattr(self, k, v)

dct = {'q': 100, 'w': 200, 'e': 300, 'r': 400}

a = MyClass(dct)

print(a.q, a.w, a.e, a.r)

Imho there is a reason why internals such as __dict__ have two leading underscores. My general recommendation would be to avoid accessing any attributes/mehods with two leading underscores as long as possible. Also see below for a reference to the Python documentation considering this topic.

And if you want to reduce it to a one-liner, you could also use a "dummy" list comprehension. The underscore catches the None return, but it is not necessarily required.

class MyClass:
    def __init__(self, new_vals):
        _ = [setattr(self, k, v) for k, v in new_vals.items()]

A short reference to the Python documentation on manipulating the built in __dict__ attribute:

A special attribute of every module is __dict__. This is the dictionary containing the module’s symbol table. Modifying this dictionary will actually change the module’s symbol table, but direct assignment to the __dict__ attribute is not possible (you can write m.__dict__['a'] = 1, which defines m.a to be 1, but you can’t write m.__dict__ = {}). Modifying __dict__ directly is not recommended.

see Python documentation on built in types

7 Comments

sooo with that one I can have my old constructor, but after I assign all values I can check if it has more keys than the "fresh" instance needed, and if it does I can update others... I like it, thank you :)
Yep, I've seen the first one before. But since the reference to the doc in the first link was for python 2, I thought that an "updated" link to the python 3 doc is a nice addition. :)
I was also referring to it pointing out that the warning is for a different context that probably doesn't apply to this usage, and that it claims that its inclusion in the official docs is akin to endorsement. The second link also has the same usage, only pointing out the preferral for not depending on **kwargs style input unless it is actually necessary (which would apply to the other answer, but does not seem to be saying that using __dict__.update is inherently problematic).
Do you have any other reasonings for why __dict__ should be left untouched even in cases where it simplifies implementation like this one, other than that it's named (and exposed) in such a way that implies that it is internal or private (which is a reason in itself, but not as compelling or convincing as a more authoritive source or a concrete example of failure or inferior elegance in syntax)?
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.