On Fri, Jan 22, 2016 at 10:14:57PM -0600, boB Stepp wrote: > On Thu, Jan 21, 2016 at 4:57 AM, Steven D'Aprano <st...@pearwood.info> wrote:
> > def spam(x, y): > > ... > > > > spam.extra_info = "whatever" > > A new thing that I did not suspect I could do. This bothers me for two > reasons: > > 1) It does not seem right adding attributes to functions outside > of its definition. Well, unfortunately there's no syntax for adding attributes to a function *inside* its definition. If you put it inside the body of the function, it won't get run until you call the function: def spam(arg): spam.widget = some_widget spam.widget # Fails because the function hasn't been run yet. If you put it before the function definition, the function doesn't exist yet: spam.widget = some_widget # Fails because spam doesn't exist. def spam(arg): ... So the only thing left is to put it *after* the definition. In practice, I would create a decorator that adds the extra attribute, so you can write: def add_widget(the_widget): def decorator(func): func.widget = the_widget return func return decorator @add_widget(some_widget) def spam(arg): ... > 2) spam.extra_info appears to be global: `spam` is global, but only because you created it in the global namespace. You could have put it in a class, or nested inside another function. `extra_info` is not global, it is attached firmly to `spam`, no different from the 30 or so existing attributes of functions: py> dir(lambda:None) ['__annotations__', '__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__get__', '__getattribute__', '__globals__', '__gt__', '__hash__', '__init__', '__kwdefaults__', '__le__', '__lt__', '__module__', '__name__', '__ne__', '__new__', '__qualname__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__'] or for that matter nearly any object. Even None has 20+ attributes. You'll notice that *by default* all of the function attributes are __dunder__ names, which mean that they are used (somewhere) by the Python implementation. E.g. the __code__ object contains the actual byte-code of the function. But you can add your own, if you have a reason to do so. It would have been very easy for the core developers to prohibit the addition of arbitrary attributes to functions: just don't give functions a __dict__ attribute. And indeed, in the earliest versions of Python, such as version 1.5, functions didn't have a __dict__ and you couldn't add attributes to them. So it is a deliberate choice to allow adding attributes to functions, but not strings, ints, floats etc. [...] > And I imagine I am being dense about something that is quite obvious > to you: How is this a useful feature to have? What does it give me > that is more useful than just saying something like: > > just_another_global variable = "whatever" > > ? It keeps the function's data close to the function. What if you have two functions, spam and eggs, that both want to claim the name "widget" for their internal data? Do they just stomp all over each other's data? Or do you call them "spam_widget" and "eggs_widget"? If you can write spam_widget, what's wrong with writing spam.widget instead? > And what bothered me about my original example that started this > thread is that when my typo > > humdrum.sigh_strenght = 'high' > > was accepted and did not generate an error, it seemed to mean to me > that I was violating my object's data encapsulation. That's a matter of opinion. Encapsulation just means that data and code that operates on that data are bundled together in some sense. Encapsulation isn't even *necessarily* to do with objects. Ancient 1970s BASICs even had a primitive form of encapsulation, with the DATA statement. You could encapsulate data in the same file as the BASIC code that operated on that data. One question is whether objects are *open* or *closed* to modifications. Python defaults to them being open unless there are good reasons for them to be closed. Other languages, like Java, default to them being closed: you can't easily add new attributes to Java objects after creation. > It just seems to > me that I should not be able to arbitrarily add new attributes from > outside the class definition that created my object. That seems > similar to having a class Dog, to which from outside the class' > definition, I decide to add a new Dog attribute that all dogs in this > class can now have a tail sticking out of their noses. That would be: lassie = Dog() lassie.nose.tail = make_tail() Yes, that would be a silly thing to do. But how about this? lassie = Dog() lassie.collar = Collar() I don't think that collar should be an attribute of all dogs. I know that Gaspode wouldn't be caught dead wearing a collar: http://wiki.lspace.org/mediawiki/index.php/Gaspode and I'm pretty sure that neither White Fang nor Buck would have collars: https://en.wikipedia.org/wiki/White_Fang https://en.wikipedia.org/wiki/The_Call_of_the_Wild "collar" is not an attribute of the Dog class, but it might be an attribute of individual dogs. -- Steve _______________________________________________ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor