Is adding attributes dynamically frowned upon in Python?
Asked Answered
P

3

9

In Python, you can assign an arbitrary attribute from outside the defining class:

class Profile(models.Model):
    user = models.OneToOneField(User)  
    name = models.CharField(max_length=140)

p = Profile()
p.age = 42

The underlying mechanism here is __dict__ attribute that maintains a dictionary of all attributes.

We were all told not to expose our inner workings to the client code, but attaching new data doesn't have to do with encapsulation at all, right? Is this idiom common for Python code?


Just What I Mean…

Each Tweet has standard fields, like id, text, owner.
When returning tweet list for a user, you want to display if a tweet is “favorited” by this user.

Obviously, to obtain is_favorite you need to query many-to-many relationship for this user.
Would it be OK to pre-fill Tweet objects with is_favorite corresponding to current user?

Sure I could expose a method is_favorite_for(user) but I'm hitting Django template language limitations that doesn't allow to call methods with arguments from inside the template. Also, I believe a template should not be calling methods at all.


I know this will work fine, but I wonder if doing something like that in an open source project would get other developers to look on me with contempt.

Sidenote:

I come from C#/.NET background where dynamic types were introduced very recently and aren't adapted widely except for some niche areas (interoperability, IoC frameworks, REST client frameworks, etc).

Pouter answered 28/10, 2011 at 16:34 Comment(0)
E
9

My view is that it is a bad practice.

The object doesn't know that you're messing with its attributes. Consider, for example, what would happen if Profile were later expanded to have an attribute called age, unrelated to p.age in your code.

If you want to add attributes, why not subclass Profile, or have an external mapping of Profiles to an object with your custom attributes?

Enid answered 28/10, 2011 at 16:40 Comment(5)
though you do get the same pitfall with subclassingChopfallen
@Owen: True. IMO the key difference is that with subclassing, the problem is likely to be easier to locate since it's between the base class and the subclass. Adding attributes externally means that the problem code can be anywhere in the codebase.Enid
I expanded a bit on the reasons, I think a Tweet and is_favorite is a better example than Person and age here. Subclassing wouldn't make much sense in this case, right?Pouter
@DanAbramov: I am not qualified to comment on Django's limitations, but in plain Python I'd probably put the user's favourites into a set rather than making is_favourite an attribute of Tweet.Enid
This makes sense. If getting favorites is too verbose (in my real app, there are two layers of many-to-many relationships), I could just fetch them into a list in the code and pass them to the template, instead of making is_favorite an attribute.Pouter
K
5

I think the answer is: It depends. First, if you really want to prevent it you can by defining __slots__ in the class. And it is not generally a good practice to add attributes not actually defined in the class, as it can be confusing to someone reading the code and is rarely useful.

But at certain times, it is useful to be able to do this and Python documentation discusses this as a way to get something similar to a C struct or Pascal Record (see http://docs.python.org/tutorial/classes.html under section 9.7 Odds and Ends.)

Kentiga answered 28/10, 2011 at 16:42 Comment(0)
W
3

If the attribute is only there sometimes, you risk getting an AttributeError out of nowhere for one object while the code worked fine for another object of the same class (yes, exact types aren't that important when duck-typing, but objects of the same class are frequently assumed to be of the same "duck type"). Even if it doesn't happen, you can't be sure just by looking at part of the code, and it's much harder to check in any case. So, doing this only makes your code less reliable.

Then there's the option of providing a default attribute as class attribute or property, only assigning an object attribute when it differs from the default. But for stuff that is expected to vary per object, the clarity of having every attribute ever listed in __init__ usually outweights any potential advantages of delaying instance attribute access.

That is not to say it's not acceptable, but you'd have to make a compelling argument for it to be considered a good idea.

Wench answered 28/10, 2011 at 16:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.