问题
I've been reading about how make Python classes less dynamic, specifically by not allowing users to dynamically create new attributes. I've read that overloadding __setattr__ is a good way to do this , and __slots__ is not the way to go. One post on this last thread actually suggests that __slots__
can break pickling. (Can anyone confirm this?)
However, I was just reading the whatsnew for Python 2.2, and the attribute access section actually suggests using __slots__
for the very purpose of constraining attribute creation, not just for optimization like others have suggested. In terms of Python history, does anyone know what the original intention of __slots__
was? Is constraining variable creation a feature or a bug to be abused? How have people seen __slots__
used in practice? Have many people seen __setattr__
overloaded to restrict attribute creation? Which one is best? If you are more familiar with one method or the other, feel free to post the pros and cons of the method you know. Also, if you have a different way of solving the problem, please share! (And please try not to just repeat the downsides of __slots__
that have been expressed in other threads.)
EDIT: I was hoping to avoid discussion of "why?", but the first answer indicates that this is going to come up, so I'll state it here. In the project in question, we're using these classes to store "configuration information", allowing the user to set attributes on the objects with their (the users') parameters, and then pass the objects off to another part of the program. The objects do more than just store parameters, so a dictionary wouldn't work. We've already had users accidentally type an attribute name wrong, and end up creating a new attribute rather than setting one of the attributes that the program expects. This goes undetected, and so the user thinks they're setting the parameter but doesn't see the expected result. This is confusing for the user, and hard to spot. By constraining attribute creation, an exception will be thrown rather than pass by silently.
EDIT 2, re pickling: These objects will be something that we will want to store in the future, and pickling seems like a good way to do this. If __slots__
is clearly the best solution, we could probably find another way to store them, but pickling would definitely be of value, and should be kept in consideration.
EDIT 3: I should also mention that memory conservation isn't an issue. Very few of these objects will be created, so any memory saved will be negligible (like 10s of kilobytes on a 3-12 GB machine).
回答1:
My suggestion for your use case is to use __setattr__
and issue a warning when the attribute name is not recognized using Python's warnings module.
回答2:
Why are you trying to restrict developers from doing that? Is there any technical reason besides "I don't want them to do it" for it? If not, don't do it.
Anyway, using __slots__
saves memory (no __dict__
) so it's the better solution to do it. You won't be able to use it if some of your code needs the object to have a __dict__
though.
If there is a good reason to restrict it (again, are you sure there is one?) or you need to save that little bit of memory, go for __slots__
.
After reading your explanation you might want to use __setattr__
, possibly combined with __slots__
(you need to store the attribute whitelist somewhere anyway, so you can as well use it to save memory). That way you can display some more helpful information such as which similar attribute are available. A possible implementation could look like this:
class Test(object):
__slots__ = ('foo', 'bar', 'moo', 'meow', 'foobar')
def __setattr__(self, name, value):
try:
object.__setattr__(self, name, value)
except AttributeError:
alts = sorted(self.__slots__, key=lambda x: levenshtein(name, x))
msg = "object has no attribute '{}', did you mean '{}'?"
raise AttributeError(msg.format(name, alts[0]))
The levenshtein()
I tested it with was implementation #4 from this site. In case of not so smart users you might want to make the error even more verbose and include all close matches instead of just the first one.
You can further improve the code by creating a mixin class containing just the __setattr__
method. That way you can keep the code out of your real classes and also have a custom __setattr__
if necessary (just use super(...).__setattr__(...)
in the mixin instead of object.__setattr__
)
来源:https://stackoverflow.com/questions/20406363/setattr-versus-slots-for-constraining-attribute-creation-in-python