Object customization (was: Arbitrary attributes on funcs and methods)
Obviously, the user-attr proposal is not so "simple" as it looks like. I wish we all realize what's really going on here. In all cited use cases for this proposal, functions are no more perceived as functions per se, but as data structures (objects) which are the target of the computation. IOW, functions are just considered as *instances* of a class (inheriting from the builtin "PyFunction" class) with user-attributes, having a state, and eventually a set of operations bound to them. I guess that everybody realized that with this proposal, one could bind not only doc strings, but also functions to the function. def func(): pass def serialize(): ... func.pack = serialize func.pack() What is this? This is manual instance customization. Since nobody said that this would be done 'exceptionally', but rather on a regular basis for all functions (and generally, for all objects) in a program, the way to customize instances after the fact, makes those instances singletons of user-defined classes. You may say "so what?". Well, this is fine if it were part of the object model from the start. And there's no reason why only functions and methods can have this functionality. Stick the __dict__ slot in the object header and let me bind user-attributes to all objects. I have a favorite number, 7, so I want to label it Vlad's number. seven = 7; seven.fanclub = ['Vlad']. I want to add a boolean func to all numbers, n.is_prime(). I want to have a s.zip() method for a set of strings in my particular application, not only the builtin ones. Why is it not allowed to have this today? Think about it! How would you solve your app needs today? Through classes and instances. That's the prescribed `legal' way to do customized objects; no shortcuts. Saying that mucking with functions' __doc__ strings is the only way to implement some functionality is simply not true. In short, there's no way I can accept this proposal in its current state and make the distingo between functions/methods and other kinds of objects (including 3rd party ones). If we're to go down this road, treat all objects as equal citizens in this regard. None or all. The object model must remain consistent. This proposal opens a breach in it. And not the lightest! And this is only part of the reasons why I'm still firmly -1 until P3K. Glad to see that Barry exposed some of the truth about it, after preserving our throats, i.e. he understood that we understood that he fully understood the power of namespaces, but eventually decided to propose a fraction of a significant change reserved for the next major Python release... wink <wink object at 80c5f30>
wink.fraction = 1e+-1 wink.fraction.precision = 1e-+1 wink.compute() 0.0
-- Vladimir MARANGOZOV | Vladimir.Marangozov@inrialpes.fr http://sirac.inrialpes.fr/~marangoz | tel:(+33-4)76615277 fax:76615252
Vladimir Marangozov wrote:
Obviously, the user-attr proposal is not so "simple" as it looks like.
This is not obvious to me. Both the concept and implementation appear fairly simple to me.
I wish we all realize what's really going on here.
In all cited use cases for this proposal, functions are no more perceived as functions per se, but as data structures (objects) which are the target of the computation. IOW, functions are just considered as *instances* of a class (inheriting from the builtin "PyFunction" class) with user-attributes, having a state, and eventually a set of operations bound to them.
I don't see why they aren't still functions. Putting a rack on my bicycle doesn't make it a pickup truck. I think it's a real stretch to say they would become "instances of a class". There's no inheritance, and the "state" isn't visible inside the function (probably unfortunately <wink>). Just like today, they are objects of type PyFunction, and they get called the same old way. You'll be able to hang extra stuff off them, just like today you can hang extra stuff off a module object without the module's knowledge or cooperation.
I guess that everybody realized that with this proposal, one could bind not only doc strings, but also functions to the function.
def func(): pass def serialize(): ... func.pack = serialize func.pack()
What is this? This is manual instance customization.
What is "def"? What is f.__doc__ = ... ?
Since nobody said that this would be done 'exceptionally', but rather on a regular basis for all functions (and generally, for all objects) in a program, the way to customize instances after the fact, makes those instances singletons of user-defined classes.
Only according to a very loose definition of "instance" and "user-defined class". More accurately, they are objects as they always have been (oops, Barry screwed up the time- machine again; please adjust the tenses of the above).
You may say "so what?". Well, this is fine if it were part of the object model from the start. And there's no reason why only functions and methods can have this functionality. Stick the __dict__ slot in the object header and let me bind user-attributes to all objects.
Perceived need is part of this.
I have a favorite number, 7, so I want to label it Vlad's number. seven = 7; seven.fanclub = ['Vlad']. I want to add a boolean func to all numbers, n.is_prime(). I want to have a s.zip() method for a set of strings in my particular application, not only the builtin ones.
Why is it not allowed to have this today? Think about it!
This is apparently a reducto ad absurdum argument. It's got the absurdum, but not much reducto. I prefer this one: Adding attributes to functions is immoral. Therefore func.__doc__ is immoral and should be removed. For another thing, we'll need a couple generations to argue about what to do with those 100 singleton int objects <wink>.
How would you solve your app needs today? Through classes and instances. That's the prescribed `legal' way to do customized objects; no shortcuts. Saying that mucking with functions' __doc__ strings is the only way to implement some functionality is simply not true.
No, it's a matter of convenience. Remember, Pythonistas are from Yorkshire ("You had Python??... You had assembler??.. You had front-panel toggle switches??.. You had wire- wrapping tools??..").
In short, there's no way I can accept this proposal in its current state and make the distingo between functions/methods and other kinds of objects (including 3rd party ones). If we're to go down this road, treat all objects as equal citizens in this regard. None or all.
They are all first class objects already. Adding capabilities to one of them doesn't subtract them from any other.
The object model must remain consistent. This proposal opens a breach in it. And not the lightest!
In any sense in which you can apply the word "consistent" to Python's object model, I fail to see how this makes it less so.
And this is only part of the reasons why I'm still firmly -1 until P3K. Glad to see that Barry exposed some of the truth about it, after preserving our throats, i.e. he understood that we understood that he fully understood the power of namespaces, but eventually decided to propose a fraction of a significant change reserved for the next major Python release... wink <wink object at 80c5f30>
wink.fraction = 1e+-1 wink.fraction.precision = 1e-+1 wink.compute() 0.0
I don't see anything here but an argument that allowing attributes on function objects makes them vaguely similar to instance objects. To the extent that I can agree with that, I fail to see any harm in it. - Gordon
Gordon> I don't see why they aren't still functions. Putting a rack on Gordon> my bicycle doesn't make it a pickup truck. Though putting a gun in the rack might... ;-) Skip
Skip Montanaro writes:
Though putting a gun in the rack might... ;-)
And make sure that rack is big enough for the dogs, we don't want them to feel left out! (Gosh, I'm feeling like I'm back in south-west Virginia already! ;) -Fred -- Fred L. Drake, Jr. <fdrake at acm.org> Corporation for National Research Initiatives
Fred> Skip Montanaro writes: >> Though putting a gun in the rack might... ;-) Fred> And make sure that rack is big enough for the dogs, we don't want Fred> them to feel left out! They fit in the panniers. (They're minature german shorthair pointers...) extending-this-silliness-ly y'rs... Skip
Gordon McMillan wrote:
I don't see anything here but an argument that allowing attributes on function objects makes them vaguely similar to instance objects. To the extent that I can agree with that, I fail to see any harm in it.
To the extent it encourages confusion, I think it sucks.
def this(): ... sucks = "no" ... this.sucks = "yes"
print this.sucks 'yes'
Why on earth 'sucks' is not the object defined in the function's namespace? Who made that deliberate decision? Clearly 'this' defines a new namespace, so it'll be also legitimate to get a NameError, or to:
print this.sucks 'no'
Don't you think? And don't explain to me that this is because there's a code object, different from the function object, which is compiled at the function's definition, then assotiated with the function object, blah, blah, blah... -- Vladimir MARANGOZOV | Vladimir.Marangozov@inrialpes.fr http://sirac.inrialpes.fr/~marangoz | tel:(+33-4)76615277 fax:76615252
Vladimir Marangozov wrote:
Gordon McMillan wrote:
I don't see anything here but an argument that allowing attributes on function objects makes them vaguely similar to instance objects. To the extent that I can agree with that, I fail to see any harm in it.
To the extent it encourages confusion, I think it sucks.
def this(): ... sucks = "no" ... this.sucks = "yes"
print this.sucks 'yes'
Why on earth 'sucks' is not the object defined in the function's namespace?
Because that one is a local. Python allows the same name in different places. Used wisely, it's a handy feature of namespaces.
Who made that deliberate decision?
What decision? To put a name "sucks" both in the function's locals and as a function attribute? To print something accessed with object.attribute notation in the obvious manner? Deciding not to cause gratuitous UnboundLocalErrors? This is nowhere near as confusing as, say, putting a module named X in a package named X and then saying "from X import *", (hi, Marc-Andre!).
Clearly 'this' defines a new namespace, so it'll be also legitimate to get a NameError, or to:
print this.sucks 'no'
Don't you think?
Only if you've done "this.sucks = 'no'". Or are you saying that if functions have attributes, people will all of a sudden expect that function locals will have initialized and maintained state? We certainly get plenty of newbie confusion about namespaces, assignment and scoping; maybe I've seen one or two where people thought function.local should be legal (do Python-tutors see this?). In those cases, is it the existence of function.__doc__ that causes the confusion? If yes, and this is a serious problem, then you should be arguing for the removal of __doc__. If not, why would allowing adding more attributes exacerbate the problem?
And don't explain to me that this is because there's a code object, different from the function object, which is compiled at the function's definition, then assotiated with the function object, blah, blah, blah...
No problem. [Actually, the best argument against this I can see is that functional-types already try to use function objects where any sane person knows you should use an instance; and since this doesn't further their agenda, the bastard's will just scream louder <wink>]. - Gordon
Gordon McMillan wrote:
... This is nowhere near as confusing as, say, putting a module named X in a package named X and then saying "from X import *", (hi, Marc-Andre!).
Users shouldn't bother looking into packages... only at the documented interface ;-) The hack is required to allow sibling submodules to import the packages main module (I could have also written import __init__ everywhere but that wouldn't have made things clearer), BTW. It turned out to be very convenient during development of all those mx packages. -- Marc-Andre Lemburg ______________________________________________________________________ Business: http://www.lemburg.com/ Python Pages: http://www.lemburg.com/python/
[Gordon McMillan]
... Or are you saying that if functions have attributes, people will all of a sudden expect that function locals will have initialized and maintained state?
I expect that they'll expect exactly what happens in JavaScript, which supports function attributes too, and where it's often used as a nicer-than-globals way to get the effect of C-like mutable statics (conceptually) local to the function. BTW, viewing this all in OO terms would make compelling sense only if Guido viewed everything in OO terms -- but he doesn't. To the extent that people must <wink>, Python doesn't stop you from adding arbitrary unique attrs to class instances today either. consistent-in-inconsistency-ly y'rs - tim
Tim Peters wrote:
[Gordon McMillan]
... Or are you saying that if functions have attributes, people will all of a sudden expect that function locals will have initialized and maintained state?
I expect that they'll expect exactly what happens in JavaScript, which supports function attributes too, and where it's often used as a nicer-than-globals way to get the effect of C-like mutable statics (conceptually) local to the function.
so it's no longer an experimental feature, it's a "static variables" thing? umm. I had nearly changed my mind to a "okay, if you insist +1", but now it's back to -1 again. maybe in Py3K... </F>
"FL" == Fredrik Lundh <effbot@telia.com> writes:
FL> so it's no longer an experimental feature, it's a "static FL> variables" thing? FL> umm. I had nearly changed my mind to a "okay, if you insist FL> +1", but now it's back to -1 again. maybe in Py3K... C'mon! Most people are still going to just use module globals for function statics because they're less to type (notwithstanding the sometimes-optional global decl). You can't worry about all the novel abuses people will think up for this feature -- they're already doing it with all sorts of other things Pythonic, e.g. docstrings, global as pragma, etc. Can I get at least a +0? :) -Barry
Mark> Im quite amazed this is contentious! Definately a +1 from me! +1 from the skippi in Chicago as well... Skip
Barry Wrote:
Can I get at least a +0? :)
okay, I'll retract. here's today's opinion: +1 on an experimental future, which is not part of the language definition, and not necessarily supported by all implementations. (and where supported, not necessarily very efficient). -1 on static function variables implemented as attributes on function or method objects. def eff(): "eff" print "eff", eff.__doc__ def bot(): "bot" print "bot", bot.__doc__ eff() bot() eff, bot = bot, eff eff() bot() # or did your latest patch solve this little dilemma? # if so, -1 on your patch ;-) </F>
"FL" == Fredrik Lundh <fredrik@pythonware.com> writes:
FL> # or did your latest patch solve this little dilemma? No, definitely not.
"AMK" == Andrew M Kuchling <akuchlin@mems-exchange.org> writes:
AMK> Wait, the attributes added to a function are visible inside AMK> the function? My patch definitely does not change Python's scoping rules in any way. This was a 1/2 hour hack, for Guido's sake! :) -Barry
Fredrik Lundh wrote:
-1 on static function variables implemented as attributes on function or method objects.
def eff(): "eff" print "eff", eff.__doc__
def bot(): "bot" print "bot", bot.__doc__
eff() bot()
eff, bot = bot, eff
eff() bot()
# or did your latest patch solve this little dilemma? # if so, -1 on your patch ;-)
To belabor the obvious (existing Python allows obsfuction), I present: class eff: "eff" def __call__(self): print "eff", eff.__doc__ class bot: "bot" def __call__(self): print "bot", bot.__doc__ e = eff() b = bot() e() b() eff, bot = bot, eff e = eff() b = bot() e() b() There's nothing new here. Why does allowing the ability to obsfucate suddenly warrant a -1? - Gordon
To belabor the obvious (existing Python allows obsfuction), I present:
class eff: "eff" def __call__(self): print "eff", eff.__doc__
class bot: "bot" def __call__(self): print "bot", bot.__doc__
e = eff() b = bot() e() b()
eff, bot = bot, eff e = eff() b = bot() e() b()
There's nothing new here. Why does allowing the ability to obsfucate suddenly warrant a -1?
since when did Python grow full lexical scoping? does anyone that has learned about the LGB rule expect the above to work? in contrast, my example used a name which appears to be defined in the same scope as the other names introduced on the same line of source code -- but isn't. def foo(x): foo.x = x here, "foo" doesn't refer to the same namespace as the argument "x", but to instead whatever happens to be in an entirely different namespace at the time the function is executed. in other words, this feature cannot really be used to store statics -- it only looks that way... </F>
since when did Python grow full lexical scoping?
does anyone that has learned about the LGB rule expect the above to work?
Not sure what LGB stands for. "Local / Global / Built-in"?
in contrast, my example used a name which appears to be defined in the same scope as the other names introduced on the same line of source code -- but isn't.
def foo(x): foo.x =3D x
here, "foo" doesn't refer to the same namespace as the argument "x", but to instead whatever happens to be in an entirely different namespace at the time the function is executed.
in other words, this feature cannot really be used to store statics -- it only looks that way...
Huh. ?? I'm assuming your hypothetical foo.x means the attribute 'x' of the function 'foo' in the global namespace for the function 'foo' - which, conveniently, is the module where foo is defined! 8<--- foo.py --->8 def foo(): # Return the object named 'foo'. return foo 8<--- end foo.py --->8 8<--- bar.py --->8 from foo import * print foo() 8<--- end bar.py --->8 % python bar.py <function foo at 80eb4c0> % I must be misapprehending what you're suggesting - i know you know this stuff better than i do - but it seems to me that foo.x would work, were foo to have an x. (And that foo.x would, in my esteem, be a suboptimal way to get at x from within foo, but that's besides the fact.) Ken klm@digicool.com
Ken Manheimer <klm@digicool.com> wrote:
does anyone that has learned about the LGB rule expect the above to work?
Not sure what LGB stands for. "Local / Global / Built-in"?
certain bestselling python books are known to use this acronym...
I'm assuming your hypothetical foo.x means the attribute 'x' of the function 'foo' in the global namespace for the function 'foo' - which, conveniently, is the module where foo is defined!
did you run the eff() bot() example?
I must be misapprehending what you're suggesting - i know you know this stuff better than i do - but it seems to me that foo.x would work, were foo to have an x.
sure, it seems to be working. but not for the right reason.
(And that foo.x would, in my esteem, be a suboptimal way to get at x from within foo, but that's besides the fact.)
fwiw, I'd love to see a good syntax for this. might even change my mind... </F>
>> (And that foo.x would, in my esteem, be a suboptimal way to get at x >> from within foo, but that's besides the fact.) Fredrik> fwiw, I'd love to see a good syntax for this. might even Fredrik> change my mind... Could we overload "_"'s meaning yet again (assuming it doesn't already have a special meaning within functions)? That way def bar(): print _.x def foo(): print _.x foo.x = "public" bar.x = "private" bar, foo = foo, bar foo() would display private on stdout. *Note* - I would not advocate this use be extended to do a more general lookup of attributes - it should just refer to attributes of the function of which the executing code object is an attribute. (It may not even be possible.) (I've never used _ for anything, so I don't know all its current (ab)uses. This is just a thought that occurred to me...) Skip
On Fri, 14 Apr 2000, Barry A. Warsaw wrote:
"FL" == Fredrik Lundh <effbot@telia.com> writes:
FL> fwiw, I'd love to see a good syntax for this. might even FL> change my mind...
def foo(x): self.x = x
? <ducking> :)
Hehe... actually, I'd take Skip's "_.x = x" over the above suggestion. The above syntax creates too much of an expectation to look for "self". There would, of course, be problems that self.x doesn't work in a method while _.x could. Cheers, -g -- Greg Stein, http://www.lyra.org/
Greg Stein <gstein@lyra.org> wrote:
On Fri, 14 Apr 2000, Barry A. Warsaw wrote:
> "FL" == Fredrik Lundh <effbot@telia.com> writes:
FL> fwiw, I'd love to see a good syntax for this. might even FL> change my mind...
def foo(x): self.x = x
? <ducking> :)
Hehe... actually, I'd take Skip's "_.x = x" over the above suggestion. The above syntax creates too much of an expectation to look for "self". There would, of course, be problems that self.x doesn't work in a method while _.x could.
how about the obvious one: adding the name of the function to the local namespace? def foo(x): foo.x = x (in theory, this might of course break some code. but is that a real problem?) after all, my concern is that the above appears to work, but mostly by accident:
def foo(x): foo.x = x foo(10) foo.x 10 # cool. now let's try this on a method class Foo: def bar(self, x): bar.x = x foo = Foo() foo.bar(10) Traceback (most recent call first): NameError: bar # huh?
maybe making it work in both cases would help? ... but on third thought, maybe it's sufficient to just keep the "static variable" aspect out of the docs. I just browsed a number of javascript manuals, and I couldn't find a trace of this feature. so how about this? -0.90000000000000002 on documenting this as "this can be used to store static data in a function" +1 on the feature itself. </F>
"FL" == Fredrik Lundh <effbot@telia.com> writes:
FL> so how about this? FL> -0.90000000000000002 on documenting this as "this FL> can be used to store static data in a function" FL> +1 on the feature itself. Works for me! I think function attrs would be a lousy place to put statics anyway. -Barry
[/F]
-0.90000000000000002 on documenting this as "this can be used to store static data in a function"
-1 on that part from me. I never recommended to do it, I merely predicted that people *will* do it. And, they will.
+1 on the feature itself.
I remain +0. [Barry]
Works for me! I think function attrs would be a lousy place to put statics anyway.
Yes, but the alternatives are also lousy: a global, or abusing default args. def f(): f.n = f.n + 1 return 42 f.n = 0 ... print "f called", f.n, "times" vs _f_n = 0 def f(): global _f_n _f_n = _f_n + 1 return 42 ... print "f called", _f_n, "times" vs def f(n=[0]): n[0] = n[0] + 1 return 42 ... print "f called ??? times" As soon as s person bumps into the first way, they're likely to adopt it, simply because it's less lousy than the others on first sight.
On Sat, 15 Apr 2000, Tim Peters wrote: [Barry]
Works for me! I think function attrs would be a lousy place to put statics anyway.
[Tim Peters]
Yes, but the alternatives are also lousy: a global, or abusing default args. <snipped examples>
Personally I kind of like the alternative of a class: class _Foo: def __init__(self): self.n = 0 def f(self): self.n = self.n+1 return 42 f = _Foo().f getting-n-out-of-f-is-left-as-an-exercise-ly y'rs, Z. -- Moshe Zadka <mzadka@geocities.com>. http://www.oreilly.com/news/prescod_0300.html http://www.linux.org.il -- we put the penguin in .com
On Sat, 15 Apr 2000 bwarsaw@cnri.reston.va.us wrote:
"FL" == Fredrik Lundh <effbot@telia.com> writes:
FL> so how about this?
FL> -0.90000000000000002 on documenting this as "this FL> can be used to store static data in a function"
FL> +1 on the feature itself.
Works for me! I think function attrs would be a lousy place to put statics anyway.
Huh? Why? (I don't have a problem with omitting mention of this use - seems like encouraging the use of globals, often a mistake.) Ken klm@digicool.com
On Sat, 15 Apr 2000, Fredrik Lundh wrote:
Greg Stein <gstein@lyra.org> wrote:
On Fri, 14 Apr 2000, Barry A. Warsaw wrote:
>> "FL" == Fredrik Lundh <effbot@telia.com> writes:
FL> fwiw, I'd love to see a good syntax for this. might even FL> change my mind...
def foo(x): self.x = x
? <ducking> :)
Hehe... actually, I'd take Skip's "_.x = x" over the above suggestion. The above syntax creates too much of an expectation to look for "self". There would, of course, be problems that self.x doesn't work in a method while _.x could.
how about the obvious one: adding the name of the function to the local namespace?
def foo(x): foo.x = x
'self.x' would collide profoundly with the convention of using 'self' for the instance-argument in bound methods. Here, foo.x assumes that 'foo' is not rebound in the context of the def - the class, module, function, or wherever it's defined. That seems like an unnecessarily too strong an assumption. Both of these things suggest to me that we don't want to use a magic variable name, but rather some kind of builtin function to get the object (lexically) containing the block. It's tempting to name it something like 'this()', but that would be much too easily confused in methods with 'self'. Since we're looking for the lexically containing object, i'd call it something like current_object(). class Something: """Something's cooking, i can feel it.""" def help(self, *args): """Spiritual and operational guidance for something or other. Instructions for using help: ...""" print self.__doc__ print current_object().__doc__ if args: self.do_mode_specific_help(args) I think i'd be pretty happy with the addition of __builtins__.current_object, and the allowance of arbitrary metadata with functions (and other funtion-like objects like methods). Ken klm@digicool.com
Fredrik Lundh wrote:
To belabor the obvious (existing Python allows obsfuction), I present:
class eff: "eff" def __call__(self): print "eff", eff.__doc__
class bot: "bot" def __call__(self): print "bot", bot.__doc__
e = eff() b = bot() e() b()
eff, bot = bot, eff e = eff() b = bot() e() b()
There's nothing new here. Why does allowing the ability to obsfucate suddenly warrant a -1?
since when did Python grow full lexical scoping?
I know that's not Swedish, but I haven't the foggiest what you're getting at. Where did lexical scoping enter?
does anyone that has learned about the LGB rule expect the above to work?
You're the one who did "eff, bot = bot, eff". The only intent I can infer is obsfuction. The above works the same as yours, for whatever your definition of "work".
in contrast, my example used a name which appears to be defined in the same scope as the other names introduced on the same line of source code -- but isn't.
def foo(x): foo.x = x
I guess I'm missing something. -------snip------------ def eff(): "eff" print "eff", eff.__doc__ def bot(): "bot" print "bot", bot.__doc__ eff() bot() eff, bot = bot, eff eff() bot() -----------end----------- I guess we're not talking about the same example.
here, "foo" doesn't refer to the same namespace as the argument "x", but to instead whatever happens to be in an entirely different namespace at the time the function is executed.
in other words, this feature cannot really be used to store statics -- it only looks that way...
Again, I'm mystified. After "eff, bot = bot, eff", I don't see why 'bot() == "eff bot"' is a wrong result. Put it another way: are you reporting a bug in 1.5.2? If it's a bug, why is my example not a bug? If it's not a bug, why would the existence of other attributes besides __doc__ be a problem? - Gordon
There's nothing new here. Why does allowing the ability to obsfucate suddenly warrant a -1?
since when did Python grow full lexical scoping?
I know that's not Swedish, but I haven't the foggiest what you're getting at. Where did lexical scoping enter?
does anyone that has learned about the LGB rule expect the above to work?
You're the one who did "eff, bot = bot, eff". The only intent I can infer is obsfuction. The above works the same as yours, for whatever your definition of "work".
okay, I'll try again: in your example, the __call__ function refers to a name that is defined several levels up. in my example, the "foo" function refers to a name that *looks* like it's in the same scope as the "x" argument (etc), but isn't. for the interpreter, the examples are identical. for the reader, they're not.
Put it another way: are you reporting a bug in 1.5.2? If it's a bug, why is my example not a bug? If it's not a bug, why would the existence of other attributes besides __doc__ be a problem?
because people isn't likely to use __doc__ to store static variables? </F>
"FL" == Fredrik Lundh <effbot@telia.com> writes:
FL> because people isn't likely to use __doc__ to store FL> static variables? Okay, let's really see how much we can abuse __doc__ today. I'm surprised neither Zope nor SPARK are this evil. Why must I add the extra level of obfuscating indirection? Or are we talking about making __doc__ read-only in 1.6, or restricting it to strings only? -Barry -------------------- snip snip -------------------- import sys print sys.version def decorate(func): class D: pass doc = func.__doc__ func.__doc__ = D() func.__doc__.__doc__ = doc def eff(): "eff" print "eff", eff.__doc__.__doc__ decorate(eff) def bot(): "bot" print "bot", bot.__doc__.__doc__ decorate(bot) eff.__doc__.publish = 1 bot.__doc__.publish = 0 eff() bot() eff, bot = bot, eff eff() bot() for f in (eff, bot): print 'Can I publish %s? ... %s' % (f.__name__, f.__doc__.publish and 'yes' or 'no') -------------------- snip snip -------------------- % python /tmp/scary.py 1.5.2 (#7, Apr 16 1999, 18:24:22) [GCC 2.8.1] eff eff bot bot bot eff eff bot Can I publish bot? ... no Can I publish eff? ... yes
Mark Hammond wrote:
Can I get at least a +0? :)
Im quite amazed this is contentious! Definately a +1 from me!
Mark.
Amazed or not, it is contentious. I have the responsability to remove my veto once my concerns are adressed. So far, I have the impression that all I get (if I get anything at all -- see above) is "conveniency" from Gordon, which is nothing else but laziness about creating instances. As long as we discuss customization of objects with builtin types, the "inconsistency" stays bound to classes and instances. Add modules if you wish, but they are just namespaces. This proposal expands the customization inconsistency to functions and methods. And I am reluctant to see this happening "under the hood", without a global vision of the problem, just because a couple of people have abused unprotected attributes and claim that they can't do what they want because Python doesn't let them to. As to the object model, together with naming and binding, I say: KISS or do it right the first time. add-more-oil-to-the-fire-and-you'll-burn-your-house-<wink>-ly y'rs -- Vladimir MARANGOZOV | Vladimir.Marangozov@inrialpes.fr http://sirac.inrialpes.fr/~marangoz | tel:(+33-4)76615277 fax:76615252
Vladimir> So far, I have the impression that all I get (if I get Vladimir> anything at all -- see above) is "conveniency" from Gordon, Vladimir> which is nothing else but laziness about creating instances. No, you get function metadata. Barry's original reason for creating the patch was that the only writable attribute for functions or methods is the doc string. Multiple people are using it now to mean different things, and this leads to problems when those different uses clash. I submit that if I have to wrap methods (not functions) in classes and instantiate them to avoid being "lazy", then my code is going to look pretty horrible after applying this more than once or twice. Both Zope and John Aycock's system (SPARK?) demonstrate the usefulness of being able to attach metadata to functions and methods. All Barry is suggesting is that Python support that capability better. Finally, it's not clear to my feeble brain just how I would go about instantiating a method to get this capability today. Suppose I have class Spam: def eggs(self, a): return a and I want to attach an attribute to Spam.eggs that tells me if it is public/private in the Zope sense. Zope requires you to add a doc string to a method to declare that it's public: class Spam: def eggs(self, a): "doc" return a Fine, except that effectively prevents you from adding doc strings to your "private" methods as Greg Stein pointed out. Barry's proposal would allow the Spam.eggs author to attach an attribute to it: class Spam: def eggs(self, a): "doc" return a eggs.__zope_access__ = "private" I think the solution you're proposing is class Spam: class EggsMethod: def __call__(self, a): "doc" return a __zope_access__ = "private" eggs = EggsMethod() This seems to work, but also seems like a lot of extra baggage (and a performance hit to boot) to arrive at what seems like a very simple concept. -- Skip Montanaro | http://www.mojam.com/ skip@mojam.com | http://www.musi-cal.com/
Skip Montanaro wrote:
Barry's proposal would allow the Spam.eggs author to attach an attribute to it:
class Spam: def eggs(self, a): "doc" return a eggs.__zope_access__ = "private"
I think the solution you're proposing is
class Spam: class EggsMethod: def __call__(self, a): "doc" return a __zope_access__ = "private" eggs = EggsMethod()
This seems to work, but also seems like a lot of extra baggage (and a performance hit to boot) to arrive at what seems like a very simple concept.
If you prefer embedded definitions, among other things, you could do: __zope_access__ = { 'Spam' : 'public' } class Spam: __zope_access__ = { 'eggs' : 'private', 'eats' : 'public' } def eggs(self, ...): ... def eats(self, ...): ... or have a completely separate class/structure for access control (which is what you would do it in C, btw, for existing objects to which you can't add slots, ex: file descriptors, mem segments, etc). -- Vladimir MARANGOZOV | Vladimir.Marangozov@inrialpes.fr http://sirac.inrialpes.fr/~marangoz | tel:(+33-4)76615277 fax:76615252
On Fri, 14 Apr 2000, Vladimir Marangozov wrote:
... If you prefer embedded definitions, among other things, you could do:
__zope_access__ = { 'Spam' : 'public' }
class Spam: __zope_access__ = { 'eggs' : 'private', 'eats' : 'public' } def eggs(self, ...): ... def eats(self, ...): ...
or have a completely separate class/structure for access control (which is what you would do it in C, btw, for existing objects to which you can't add slots, ex: file descriptors, mem segments, etc).
This is uglier than attaching the metadata directly to the target that you are describing! If you want to apply metadata to functions, then apply them to the function! Don't shove them off in a separate structure. You're the one talking about cleanliness, yet you suggest something that is very poor from a readability, maintainability, and semantic angle. Ick. Cheers, -g -- Greg Stein, http://www.lyra.org/
[Skip, on attaching access control rights to function objects]
[VM]
... If you prefer embedded definitions, among other things, you could do:
__zope_access__ = { 'Spam' : 'public' }
class Spam: __zope_access__ = { 'eggs' : 'private', 'eats' : 'public' } def eggs(self, ...): ... def eats(self, ...): ...
[Greg]
This is uglier than attaching the metadata directly to the target that you are describing! If you want to apply metadata to functions, then apply them to the function! Don't shove them off in a separate structure.
You're the one talking about cleanliness, yet you suggest something that is very poor from a readability, maintainability, and semantic angle. Ick.
[Moshe]
This solution is close to what the eff-bot suggested. In this case it is horrible because of "editing effort": the meta-data and code of a function are better off together physically, so you would change it to ... [equivalent solution deleted]
In this particular use case, we're discussing access control rights which are part of some protection policy. A protection policy is a matrix Objects/Rights. It can be impemented in 3 ways, depending on the system: 1. Attach the Rights to the Objects 2. Attach the Objects to the Rights 3. Have a separate structure which implements the matrix. I agree that in this particular case, it seems handy to attach the rights to the objects. But in other cases, it's more appropriate to attach the objects to the rights. However, the 3rd solution is the one to be used when the objects (respectively, the rights) are fixed from the start and cannot be modified, and solution 2 (resp, 3) is not desirable/optimal/plausible... That's what I meant with: [VM]
or have a completely separate class/structure for access control (which is what you would do it in C, btw, for existing objects to which you can't add slots, ex: file descriptors, mem segments, etc).
Which presents an advantage: the potential to change completely the protection policy of the system in future versions of the software, because the protection implementation is decoupled from the objects' and the rights' implementation. damned-but-persistent-first-principles-again-<wink>'ly y'rs -- Vladimir MARANGOZOV | Vladimir.Marangozov@inrialpes.fr http://sirac.inrialpes.fr/~marangoz | tel:(+33-4)76615277 fax:76615252
On Sun, 16 Apr 2000 Vladimir.Marangozov@inrialpes.fr wrote: > > [Skip, on attaching access control rights to function objects] > > > [VM] > > >... > > > If you prefer embedded definitions, among other things, you could do: > > > > > > __zope_access__ = { 'Spam' : 'public' } > > > > > > class Spam: > > > __zope_access__ = { 'eggs' : 'private', > > > 'eats' : 'public' } > > > def eggs(self, ...): ... > > > def eats(self, ...): ... > > [Greg] > > This is uglier than attaching the metadata directly to the target that you > > are describing! If you want to apply metadata to functions, then apply > > them to the function! Don't shove them off in a separate structure. > [...] > In this particular use case, we're discussing access control rights > which are part of some protection policy. > A protection policy is a matrix Objects/Rights. It can be impemented > in 3 ways, depending on the system: > 1. Attach the Rights to the Objects > 2. Attach the Objects to the Rights > 3. Have a separate structure which implements the matrix. > [...] > [VM] > > > or have a completely separate class/structure for access control > > > (which is what you would do it in C, btw, for existing objects > > > to which you can't add slots, ex: file descriptors, mem segments, etc). > > Which presents an advantage: the potential to change completely the > protection policy of the system in future versions of the software, > because the protection implementation is decoupled from the objects' > and the rights' implementation. It may well make sense to have the system *implement* the rights somewhere else. (Distributed system, permissions caches in an object system, etc.) However it seems to me to make exceeding sense to have the initial intrinsic settings specified as part of the object! More generally, it is the ability to associate intrinsic metadata that is the issue, not the designs of systems that employ the metadata. Geez. And, in the case of functions, it seems to me to be outstandingly consistent with python's treatment of objects. I'm mystified about why you would reject that so adamantly! That said, i can entirely understand concerns about whether or how to express references to the metadata from within the function's body. We haven't even seen a satisfactory approach to referring to the function, itself, from within the function. Maybe it's not even desirable to be able to do that - that's an interesting question. (I happen to think it's a good idea, just requiring a suitable means of expression.) But being able to associate metadata with functions seems like a good idea, and i've seen no relevant clues in your "first principles" about why it would be bad. Ken klm@digicool.com Return-Path: <sc-publicity-return-2-python-dev=python.org@software-carpentry.com> Delivered-To: python-dev@python.org Received: from merlin.codesourcery.com (merlin.codesourcery.com [206.168.99.1]) by dinsdale.python.org (Postfix) with SMTP id 7312F1CD5A for <python-dev@python.org>; Sat, 15 Apr 2000 12:50:20 -0400 (EDT) Received: (qmail 17758 invoked by uid 513); 15 Apr 2000 16:57:54 -0000 Mailing-List: contact sc-publicity-help@software-carpentry.com; run by ezmlm Precedence: bulk X-No-Archive: yes Delivered-To: mailing list sc-publicity@software-carpentry.com Delivered-To: moderator for sc-publicity@software-carpentry.com Received: (qmail 16214 invoked from network); 15 Apr 2000 16:19:12 -0000 Date: Sat, 15 Apr 2000 12:11:52 -0400 (EDT) From: <gvwilson@nevex.com> To: sc-announce@software-carpentry.com, sc-publicity@software-carpentry.com Message-ID: <Pine.LNX.4.10.10004151146020.17967-100000@akbar.nevex.com> MIME-Version: 1.0 Content-Type: TEXT/PLAIN; charset=US-ASCII Subject: [Python-Dev] Software Carpentry entries now on-line Sender: python-dev-admin@python.org Errors-To: python-dev-admin@python.org X-BeenThere: python-dev@python.org X-Mailman-Version: 2.0beta3 List-Id: Python core developers <python-dev.python.org> First-round entries in the Software Carpentry design competition are now available on-line at: http://www.software-carpentry.com/entries/index.html Our thanks to everyone who entered; we look forward to some lively discussion on the "sc-discuss" list. Best regards, Greg Wilson Software Carpentry Project Coordinator
Ken> We haven't even seen a satisfactory approach to referring to the Ken> function, itself, from within the function. Maybe it's not even Ken> desirable to be able to do that - that's an interesting question. I hereby propose that within a function the special name __ refer to the function. You could have def fact(n): if n <= 1: return 1 return __(n-1) * n You could also refer to function attributes through __ (presuming Barry's proposed patch gets adopted): def pub(*args): if __.access == "private": do_private_stuff(*args) else: do_public_stuff(*args) ... if validate_permissions(): pub.access = "private" else: pub.access = "public" When in a bound method, __ should refer to the bound method, not the unbound method, which is already accessible via the class name. As far as lexical scopes are concerned, this won't change anything. I think it could be implemented by adding a reference to the function called __ in the local vars of each function. -- Skip Montanaro | http://www.mojam.com/ skip@mojam.com | http://www.musi-cal.com/
On Sun, 16 Apr 2000, Skip Montanaro wrote:
Ken> We haven't even seen a satisfactory approach to referring to the Ken> function, itself, from within the function. Maybe it's not even Ken> desirable to be able to do that - that's an interesting question.
I hereby propose that within a function the special name __ refer to the function. You could have
def fact(n): if n <= 1: return 1 return __(n-1) * n
You could also refer to function attributes through __ (presuming Barry's proposed patch gets adopted):
At first i thought you were kidding about using '__' because '_' was taken - on lots of terminals that i use, there is no intervening whitespace separating the two '_'s, so it's pretty hard to tell the difference between it and '_'! Now, i wouldn't mind using '_' if it's available, but guido was pretty darned against using it in my initial designs for packages - i wanted to use it to refer to the package containing the current module, like unix '..'. I gathered that a serious part of the objection was in using a character to denote non-operation syntax - python just doesn't do that. I also like the idea of using a function instead of a magic variable - most of python's magic variables are in packages, like os.environ. Ken klm@digicool.com
"SM" == Skip Montanaro <skip@mojam.com> writes:
Ken> We haven't even seen a satisfactory approach to referring to Ken> the function, itself, from within the function. Maybe it's not Ken> even desirable to be able to do that - that's an interesting Ken> question. SM> I hereby propose that within a function the special name __ SM> refer to the function. I think the syntax is fairly obscure. I'm neurtral on the whole idea of having a special way to get at the function object from within the body of the code. Also, the proposal to handle security policies using attributes attached to the function seems wrong. The access control decision depends on the security policy defined for the object *and* the authorization of the caller. You can't decide based solely on some attribute of the function, nor can you assume that every call of a function object will be made with the same authorization (from the same protection domain). Jeremy
On Mon, 17 Apr 2000, Jeremy Hylton wrote:
"SM" == Skip Montanaro <skip@mojam.com> writes:
Ken> We haven't even seen a satisfactory approach to referring to Ken> the function, itself, from within the function. Maybe it's not Ken> even desirable to be able to do that - that's an interesting Ken> question.
SM> I hereby propose that within a function the special name __ SM> refer to the function.
I think the syntax is fairly obscure. I'm neurtral on the whole idea of having a special way to get at the function object from within the body of the code.
I agree.
Also, the proposal to handle security policies using attributes attached to the function seems wrong.
This isn't the only application of function attributes. Can't throw them out because one use seems wrong :-) Cheers, -g -- Greg Stein, http://www.lyra.org/
Ken Manheimer wrote:
However it seems to me to make exceeding sense to have the initial intrinsic settings specified as part of the object!
Indeed. It makes perfect sense to have _intial_, intrinsic attributes. The problem is that currently they can't be specified for builtin objects. Skip asked for existing solutions, so I've made a quick tour on the problem, pointing him to 3).
And, in the case of functions, it seems to me to be outstandingly consistent with python's treatment of objects.
Oustandingly consistent isn't my opinion, but that's fine with both of us. If functions win this cause, the next imminent wish of all (Zope) users will be to attach (protection, or other) attributes to *all* objects: class Spam(...): """ Spam product""" zope_product_version = "2.51" zope_persistency = 0 zope_cache_limit = 64 * 1024 def eggs(self): ... def eats(self): ... How would you qualify the zope_* attributes so that only the zope_product version is accessible? (without __getattr__ tricks, since we're talking about `metadata'). Note that I don't expect an answer :-). The issue is crystal clear already. Be prepared to answer cool questions like this one to your customers.
I'm mystified about why you would reject that so adamantly!
Oops, I'll demystify you instantly, here, by summing up my posts: I'm not rejecting anything adamantly! To the countrary, I've suggested more. Greg said it quite well: Barry's proposal made me sending you signals about different issues you've probably not thought about before, yet we'd better sort them out before adopting his patch. As a member of this list, I feel obliged to share with you my concerns whenever I have them. My concerns in this case are: a) consistency of the class model. Apparently this signal was lost in outerspace, because my interpretation isn't yours. Okay, fine by me. This one will come back in Py3K. I'm already curious to see what will be on the table at that time. :-) b) confusion about the namespaces associated with a function object. You've been more receptive to this one. It's currently being discussed. c) generalize user-attributes for all builtin objects. You'd like to, but it looks expensive. This one is a compromise: it's related with sharing, copy on write builtin objects with modified user-attr, etc. In short, it doesn't seem to be on the table, because this signal hasn't been emitted before, nor it was really decrypted on python-dev. Classifying objects as light and heavy, and attributing them specific functionality only because of their "weight" looks very hairy. That's all for now. Discussing these issues in prime time here is goodness for Python and its users! Adopting the proposal in a hurry, because of the tight schedule for 1.6, isn't. It needs more maturation. Witness the length of the thread. it's-vacation-time-for-me-so-see-you-all-after-Easter'ly y'rs -- Vladimir MARANGOZOV | Vladimir.Marangozov@inrialpes.fr http://sirac.inrialpes.fr/~marangoz | tel:(+33-4)76615277 fax:76615252
"KLM" == Ken Manheimer <klm@digicool.com> writes:
KLM> It may well make sense to have the system *implement* the KLM> rights somewhere else. (Distributed system, permissions caches KLM> in an object system, etc.) However it seems to me to make KLM> exceeding sense to have the initial intrinsic settings KLM> specified as part of the object! It's not clear to me that the person writing the code is or should be the person specifying the security policy. I believe the CORBA security model separates policy definition into three parts -- security attributes, required rights, and policy domains. The developer would only be responsible for the first part -- the security attributes, which describe methods in a general way so that a security administrators can develop an effective policy for it. I suppose that function attributes would be a sensible way to do this, but it might also be accomplished with a separate wrapper object. I'm still not thrilled with the idea of using regular attribute access to describe static properties on code. To access the properties, yes, to define and set them, probably not. Jeremy
On Fri, 14 Apr 2000, Vladimir Marangozov wrote:
If you prefer embedded definitions, among other things, you could do:
__zope_access__ = { 'Spam' : 'public' }
class Spam: __zope_access__ = { 'eggs' : 'private', 'eats' : 'public' } def eggs(self, ...): ... def eats(self, ...): ...
This solution is close to what the eff-bot suggested. In this case it is horrible because of "editing effort": the meta-data and code of a function are better off together physically, so you would change it to class Spam: __zope_access__ = {} def eggs(self): pass __zope_access__['eggs'] = 'private' def eats(self): pass __zope_access__['eats'] = 'public' Which is way too verbose. Especially, if the method gets thrown around, you find yourself doing things like meth.im_class.__zope_access__[meth.im_func.func_name] Instead of meth.__zope_access__ And sometimes you write a function: def do_something(self): pass And the infrastructure adds the method to a class of its choice. Where would you stick the attribute then? -- Moshe Zadka <mzadka@geocities.com>. http://www.oreilly.com/news/prescod_0300.html http://www.linux.org.il -- we put the penguin in .com
Vladimir Marangozov wrote:
Amazed or not, it is contentious. I have the responsability to remove my veto once my concerns are adressed. So far, I have the impression that all I get (if I get anything at all -- see above) is "conveniency" from Gordon, which is nothing else but laziness about creating instances.
I have the impression that majority of changes to Python are conveniences.
As long as we discuss customization of objects with builtin types, the "inconsistency" stays bound to classes and instances. Add modules if you wish, but they are just namespaces. This proposal expands the customization inconsistency to functions and methods. And I am reluctant to see this happening "under the hood", without a global vision of the problem, just because a couple of people have abused unprotected attributes and claim that they can't do what they want because Python doesn't let them to.
Can you please explain how "consistency" is violated? - Gordon
Gordon McMillan wrote:
[VM]
As long as we discuss customization of objects with builtin types, the "inconsistency" stays bound to classes and instances. Add modules if you wish, but they are just namespaces. This proposal expands the customization inconsistency to functions and methods. And I am reluctant to see this happening "under the hood", without a global vision of the problem, just because a couple of people have abused unprotected attributes and claim that they can't do what they want because Python doesn't let them to.
Can you please explain how "consistency" is violated?
Yes, I can. To start with and to save me typing, please reread the 1st section of Demo/metaclasses/meta-vladimir.txt about Classes. ------- Now, whenever there are two instances 'a' and 'b' of the class A, the first inconsistency is that we're allowed to assign attributes to these instances dynamically, which are not declared in the class A. Strictly speaking, if I say:
a.author = "Guido"
and if 'author' is not an attribute of 'a' after the instantiation of A (i.e. after a = A() completes), we should get a NameError. It's an inconsistency because whenever the above assignment succeeds, 'a' is no more an instance of A. It's an instance of some other class, because A prescribes what *all* instances of A have in *common*. So from here, we have to find our way in the object model and live with this 1st inconsistency. Problem: What is the class of the singleton 'a' then? Say, I need this class after the fact to build another society of objects, i.e. "clone" 'a' a hundred of times, because 'a' has dozens of attributes different than 'b'. To make a long story short, it turns out that we can build a Python class A1, having those attributes declared, then instantiate A1 hundreds of times and hopefully, let 'a' find its true identity with:
a.__class__ = A1
This is the key of the story. We *can* build, for a given singleton, its Python class, after the fact. And this is the only thing which still makes the Python class model 'relatively consistent'! If it weren't possible to build that class A1, it would have been better to stop talking about classes and a class model in Python. ("associations of typed structures with per-type binding rules" would have probably been a better term). Now to the question: how "consistency" is violated by the proposal? It is violated, because actually we *can't* build and restore the class, after the fact, of a builtin object (a funtion 'f') to which we add user attributes. We can't do it for 2 reasons, which we hope to solve in Py3K: 1) the class of 'f' is implemented in C 2) we still can't inherit from builtin classes (at least in CPython) As a consequence, we can't actually build hundreds of "clones" of 'f' by instantiating a class object. We can build them by adding manually the same attribute, but this is not OO, this is just 'binding to a namespace'. This is the true reason on why this fragile consistency is violated. Please, save me the trouble to expose the details you're missing, to each of you, where those details are omitted for simplicity. -- Vladimir MARANGOZOV | Vladimir.Marangozov@inrialpes.fr http://sirac.inrialpes.fr/~marangoz | tel:(+33-4)76615277 fax:76615252
Vladimir Marangozov wrote:
Gordon McMillan wrote:
Can you please explain how "consistency" is violated?
Yes, I can.
Strictly speaking, if I say:
a.author = "Guido"
and if 'author' is not an attribute of 'a' after the instantiation of A (i.e. after a = A() completes), we should get a NameError.
Ah. I see. Quite simply, you're arguing from First Principles in an area where I have none. I used to, but I found that all systems built from First Principles (Eiffel, Booch's methodology...) yielded 3 headed monsters. It can be entertaining (in the WWF sense). Just trick some poor sucker into saying "class method" in the C++ sense and then watch Jim Fulton deck him, the ref and half the front row. Personally, I regard (dynamic instance.attribute) as a handy feature, not as a flaw in the object model. - Gordon
Gordon McMillan wrote:
Ah. I see. Quite simply, you're arguing from First Principles
Exactly. I think that these principles play an important role in the area of computer programming, because they put the markers in the evolution of our thoughts when we're trying to transcript the real world through formal computer terms. No kidding :-) So we need to put some limits before loosing completely these driving markers. No kidding.
in an area where I have none.
too bad for you <wink>
I used to, but I found that all systems built from First Principles (Eiffel, Booch's methodology...) yielded 3 headed monsters.
Yes. This is the state Python tends to reach, btw. I'd like to avoid this madness. Put simply, if we loose the meaning of the notion of a class of objects, there's no need to have a 'class' keyword, because it would do more harm than good.
Personally, I regard (dynamic instance.attribute) as a handy feature
Gordon, I know that it's handy!
not as a flaw in the object model.
if we still pretend there is one... -- Vladimir MARANGOZOV | Vladimir.Marangozov@inrialpes.fr http://sirac.inrialpes.fr/~marangoz | tel:(+33-4)76615277 fax:76615252
On Fri, 14 Apr 2000, Vladimir Marangozov wrote:
Gordon McMillan wrote:
Ah. I see. Quite simply, you're arguing from First Principles
Exactly.
I think that these principles play an important role in the area of computer programming, because they put the markers in the evolution of our thoughts when we're trying to transcript the real world through formal computer terms. No kidding :-) So we need to put some limits before loosing completely these driving markers. No kidding.
In YOUR opinion. In MY opinion, they're bunk. Python provides me with the capabilities that I want: objects when I need them, and procedural flow when that is appropriate. It avoids obstacles and gives me freedom of expression and ways to rapidly develop code. I don't have to worry about proper organization unless and until I need it. Formalisms be damned. I want something that works for ME. Give me code, make it work, and get out of my way. That's what Python is good for. I could care less about "proper programming principles". Pragmatism. That's what I seek.
...
I used to, but I found that all systems built from First Principles (Eiffel, Booch's methodology...) yielded 3 headed monsters.
Yes. This is the state Python tends to reach, btw. I'd like to avoid this madness.
Does not. There are many cases where huge systems have been built using Python, built well, and are quite successful. And yes, there have also been giant, monster-sized Bad Python Programs out there, too. But that can be done in ANY language. Python doesn't *tend* towards that at all. Certainly, Perl does, but we aren't talking about that (until now :-)
Put simply, if we loose the meaning of the notion of a class of objects, there's no need to have a 'class' keyword, because it would do more harm than good.
Huh? What the heck do you mean by this?
...
not as a flaw in the object model.
if we still pretend there is one...
It *DOES* have one. To argue there isn't one is simply insane and argumentative. Python just doesn't have YOUR object model. Live with it. Cheers, -g -- Greg Stein, http://www.lyra.org/
On Fri, 14 Apr 2000, Vladimir Marangozov wrote:
... Now, whenever there are two instances 'a' and 'b' of the class A, the first inconsistency is that we're allowed to assign attributes to these instances dynamically, which are not declared in the class A.
Strictly speaking, if I say:
a.author = "Guido"
and if 'author' is not an attribute of 'a' after the instantiation of A (i.e. after a = A() completes), we should get a NameError.
I'll repeat what Gordon said: the current Python behavior is entirely correct, entirely desirable, and should not (can not) change. Your views on what an object model should be are not Python's views. If the person who writes "a.author =" wants to do that, then let them. Python does not put blocks in people's way, it simply presumes that people are intelligent and won't do Bad Things. There are enumerable times where I've done the following: class _blank() pass data = _blank() data.item = foo data.extra = bar func(data) It is a tremendously easy way to deal with arbitrary data on an attribute basis, rather than (say) dictionary's key-based basis.
... arguments about alternate classes and stuff ...
Sorry. That just isn't Python. Not in practice, nor in intent. Applying metadata to the functions is an entirely valid, Pythonic idea. Cheers, -g -- Greg Stein, http://www.lyra.org/
Greg Stein wrote:
Your views on what an object model should be are not Python's views.
Ehm, could you explain to me what are Python's views? Sorry, I don't see any worthy argument in your posts that would make me switch from -1 to -0. -- Vladimir MARANGOZOV | Vladimir.Marangozov@inrialpes.fr http://sirac.inrialpes.fr/~marangoz | tel:(+33-4)76615277 fax:76615252
On Sat, 15 Apr 2000, Vladimir Marangozov wrote:
Greg Stein wrote:
Your views on what an object model should be are not Python's views.
Ehm, could you explain to me what are Python's views? Sorry, I don't see any worthy argument in your posts that would make me switch from -1 to -0.
"We're all adults here." Python says that you can do what you want. It won't get in your way. Badness is not defined. If somebody wants to write "a.author='Guido'" then they can. There are a number of objects that can have arbitrary attributes. Classes, modules, and instances are a few (others?). Function objects are a proposed additional one. In all cases, attaching new attributes is fine and dandy -- no restriction. (well, you can implement __setattr__ on a class instance) Python's object model specifies a number of other behaviors, but nothing really material here. Of course, all these "views" are simply based on Guido's thoughts and the implementation. Implementation, doc, current practice, and Guido's discussions over the past eight years of Python's existence have also contributed to the notion of "The Python Way". Some of that may be very hard to write down, although I've attempted to write a bit of that above. After five years of working with Python, I'd like to think that I've absorbed and understand the Python Way. Can I state it? No. "We're all adults here" is a good one for this discussion. If you think that function attributes are bad for your programs, then don't use them. There are many others who find them tremendously handy. Cheers, -g -- Greg Stein, http://www.lyra.org/
Greg Stein wrote:
Python says that you can do what you want.
'Python' says nothing. Or are you The Voice of Python? <wink> If custom object attributes are convenient for you, then I'd suggest to generalize the concept, because I perceived it as a limitation too, but not for functions and methods. I'll repeat myself:
wink <wink object at 80c5f30> wink.fraction = 1e+-1 wink.fraction.precision = 1e-+1 wink.compute() 0.0
Has anybody noticed that 'fraction' is a float I wanted to qualify with a 'precision' attribute? Again: if we're about to go that road, let's do it in one shot. *This* is what would change my vote. I'll leave Guido to cut the butter, or to throw it all out the window. You're right Greg: I hardly can contribute more in this case, even if I wanted to. Okay, +53 :-) -- Vladimir MARANGOZOV | Vladimir.Marangozov@inrialpes.fr http://sirac.inrialpes.fr/~marangoz | tel:(+33-4)76615277 fax:76615252
Vladimir> I'll repeat myself: >>>> wink Vladimir> <wink object at 80c5f30> >>>> wink.fraction = 1e+-1 >>>> wink.fraction.precision = 1e-+1 >>>> wink.compute() Vladimir> 0.0 Vladimir> Has anybody noticed that 'fraction' is a float I wanted to Vladimir> qualify with a 'precision' attribute? Quick comment before I rush home... There is a significant cost to be had by adding attributes to numbers (ints at least). They can no longer be shared in the int cache. I think the runtime size increase would be pretty huge, as would the extra overhead in creating all those actual (small) IntObjects instead of sharing a single copy. On the other hand, functions are already pretty heavyweight objects and occur much less frequently than numbers in common Python programs. They aren't shared (except for instance methods, which Barry's patch already excludes), so there's no risk of stomping on attributes that are shared by more than one function. -- Skip Montanaro | http://www.mojam.com/ skip@mojam.com | http://www.musi-cal.com/
Skip Montanaro wrote:
Vladimir> Has anybody noticed that 'fraction' is a float I wanted to Vladimir> qualify with a 'precision' attribute?
Quick comment before I rush home... There is a significant cost to be had by adding attributes to numbers (ints at least). They can no longer be shared in the int cache. I think the runtime size increase would be pretty huge, as would the extra overhead in creating all those actual (small) IntObjects instead of sharing a single copy.
I know that. Believe it or not, I have a good image of the cost it would infer, better than yours. because I've thought about this problem (as well as other related problems yet to be 'discovered'), and have spent some time in the past trying to find a couple of solutions to them. However, I eventually decided to stop my time machine and wait for these issues to show up, then take a stance on them. And this is what I did in this case. I'm tired to lack good arguments and see incoming capitalized words. This makes no sense here. Go to c.l.py and repeat "we're all adults here" *there*, please. To close this chapter, I think that if this gets in, Python's user base will get more confused and would have to swallow yet another cheap gimmick. You won't be able to explain it well to them. They won't really understand it, because their brains are still young, inexperienced, looking for logical explanations where all notions coexist peacefully. In the long term, what you're pushing for to get your money quickly, isn't a favor. And that's why I maintain my vote. call-me-again-if-you-need-more-than-53'ly y'rs -- Vladimir MARANGOZOV | Vladimir.Marangozov@inrialpes.fr http://sirac.inrialpes.fr/~marangoz | tel:(+33-4)76615277 fax:76615252
On Sat, 15 Apr 2000, Vladimir Marangozov wrote:
Greg Stein wrote:
Python says that you can do what you want.
'Python' says nothing. Or are you The Voice of Python? <wink>
Well, yah. You're just discovering that?! :-) I meant "The Python Way" says that you can do what you want. It doesn't speak often, but if you know how to hear it... it is a revelation :-)
If custom object attributes are convenient for you, then I'd suggest to
Custom *function* attributes. Functions are one of the few objects in Python that are "structural" in their intent and use, yet have no way to record data. Modules and classes have a way to, but not functions. [ by "structure", I mean something that contributes to the structure, organization, and mechanics of your program. as opposed to data, such as lists, dicts, instances. ] And ditto what Skip said about attaching attributes to ints and other immutables. Cheers, -g -- Greg Stein, http://www.lyra.org/
On Sat, 15 Apr 2000, Vladimir Marangozov wrote:
Greg Stein wrote:
Your views on what an object model should be are not Python's views.
Ehm, could you explain to me what are Python's views? Sorry, I don't see any worthy argument in your posts that would make me switch from -1 to -0.
Note that all votes are important, but only a signal to Guido about our individual feelings on the matter. Every single person on this list could vote -1, and Guido can still implement the feature (at his peril :-). Conversely, we could all vote +1 and he can refuse to implement it. In this particular case, your -1 vote says that you really dislike this feature. Great. And you've provided a solid explanation why. Even better! Now, people can respond to your vote and attempt to get you to change it. This is goodness because maybe you voted -1 based on a misunderstanding or something unclear in the proposal (I'm talking general now; I don't believe that is the case here). After explanation and enlightenment, you could change the vote. The discussion about *why* you voted -1 is also instructive to Guido. It may raise an issue that he hadn't considered. In addition, people attempting to change your mind are also providing input to Guido. [ maybe too much input is flying around, but the principle is there :-) ] Basically, we can call them vetoes or votes. Either way, this is still Guido's choice :-) Cheers, -g -- Greg Stein, http://www.lyra.org/
Greg Stein wrote:
Note that all votes are important, but only a signal to Guido ... [good stuff deleted]
Very true. I think we've made good progress here.
Now, people can respond to your vote and attempt to get you to change it. ... After explanation and enlightenment, you could change the vote.
Or vice-versa :-) Fredrik has been very informative about the evolution of his opinions as the discussion evolved. As was I, but I don't count ;-) It would be nice if we adopt his example and send more signals to Guido, emitted with a fixed (positive or negative) or with a sinusoidal frequency. -- Vladimir MARANGOZOV | Vladimir.Marangozov@inrialpes.fr http://sirac.inrialpes.fr/~marangoz | tel:(+33-4)76615277 fax:76615252
On Fri, 14 Apr 2000, Barry A. Warsaw wrote:
Can I get at least a +0? :)
I want function attributes. (There are all sorts of occasions i need cues to classify functions for executives that map and apply them, and this seems like the perfect way to couple that information with the object. Much nicer than having to mangle the names of the functions, or create some external registry with the classifications.) And i think i'd want them even more if they were visible within the function, so i could do static variables. Why is that a bad thing? So i guess that means i'd give a +1 for the proposal as stands, with the understanding that you'd get *another* +1 for the additional feature - yielding a bigger, BETTER +1. Metadata, static vars, frameworks ... oh my!-) (Oh, and i'd suggest up front that documentation for this feature recommend people not use "__*__" names for their own object attributes, to avoid collisions with eventual use of them by python.) Ken klm@digicool.com
Ken Manheimer writes:
(Oh, and i'd suggest up front that documentation for this feature recommend people not use "__*__" names for their own object attributes, to avoid collisions with eventual use of them by python.)
Isn't that a standing recommendation for all names? -Fred -- Fred L. Drake, Jr. <fdrake at acm.org> Corporation for National Research Initiatives
On Fri, 14 Apr 2000, Fred L. Drake, Jr. wrote:
Ken Manheimer writes:
(Oh, and i'd suggest up front that documentation for this feature recommend people not use "__*__" names for their own object attributes, to avoid collisions with eventual use of them by python.)
Isn't that a standing recommendation for all names?
Yup. Personally, I use "_*" for private variables or other "hidden" type things that shouldn't be part of an object's normal interface. For example, all the stuff that the Python/COM interface uses is prefixed by "_" to denote that it is metadata about the classes rather than part of its interface. Cheers, -g -- Greg Stein, http://www.lyra.org/
Barry said "_" is effectively taken because it means something (at least when used a function?) to pygettext. How about "__" then? def bar(): print __.x def foo(): print __.x foo.x = "public" bar.x = "private" ... It has the added benefit that this usage adheres to the "Python gets to stomp on __-prefixed variables" convention. my-underscore-key-works-better-than-yours-ly y'rs, Skip
Ken Manheimer wrote:
I want function attributes. (There are all sorts of occasions i need cues to classify functions for executives that map and apply them, and this seems like the perfect way to couple that information with the object. Much nicer than having to mangle the names of the functions, or create some external registry with the classifications.)
how do you expect to find all methods that has a given attribute?
And i think i'd want them even more if they were visible within the function, so i could do static variables. Why is that a bad thing?
because it doesn't work, unless you change python in a backwards incompatible way. that's okay in py3k, it's not okay in 1.6. </F>
[Tim]
I expect that they'll expect exactly what happens in JavaScript, which supports function attributes too, and where it's often used as a nicer-than-globals way to get the effect of C-like mutable statics (conceptually) local to the function.
[/F]
so it's no longer an experimental feature, it's a "static variables" thing?
Yes, of course people will use it to get the effect of function statics. OK by me. People do the same thing today with class data attributes (i.e., to get the effect of mutable statics w/o polluting the module namespace). They'll use it for all sorts of other stuff too -- it's mechanism, not policy. BTW, I don't think any "experimental feature" has ever been removed -- only features that weren't experimental. So if you want to see it go away ...
umm. I had nearly changed my mind to a "okay, if you insist +1", but now it's back to -1 again. maybe in Py3K...
Greg gave the voting rule as:
-1 "Veto. And <HERE> is my reasoning."
Vladimir has done some reasoning, but the basis of your objection remains a mystery. We should be encouraging our youth to submit patches with their crazy ideas <wink>.
Yes, of course people will use it to get the effect of function statics. OK by me. People do the same thing today with class data attributes (i.e., to
Wait, the attributes added to a function are visible inside the function? (I haven't looked that closely at the patch?) That strikes me as a much more significant change to Python's scoping, making it local, function attribute, then global scope. a I thought of the attributes as labels that could be attached to a callable object for the convenience of some external system, but the function would remain blissfully unaware of the external meaning attached to itself. -1 from me if a function's attributes are visible to code inside the function; +0 if they're not. -- A.M. Kuchling http://starship.python.net/crew/amk/ The paradox of money is that when you have lots of it you can manage life quite cheaply. Nothing so economical as being rich. -- Robertson Davies, _The Rebel Angels_
>> Yes, of course people will use it to get the effect of function >> statics. OK by me. People do the same thing today with class data >> attributes (i.e., to AMK> Wait, the attributes added to a function are visible inside the AMK> function? (I haven't looked that closely at the patch?) No, they aren't. There is no change of Python's scoping rules using Barry's function attributes patch. In fact, they are *only* available to the function itself via the function's name in the module globals. That's why Fredrik's "eff, bot = bot, eff" trick worked as it did. -- Skip Montanaro | http://www.mojam.com/ skip@mojam.com | http://www.musi-cal.com/
[Tim]
Yes, of course people will use it to get the effect of function statics. OK by me. People do the same thing today with class data attributes (i.e., to
[Andrew M. Kuchling]
Wait, the attributes added to a function are visible inside the function?
No, same as in JavaScript, you need funcname.attr, just as you need classname.attr in Python today to fake the effect of mutable class statics (in the C++ sense).
[hysteria deleted <wink>] ... +0 if they're not.
TimBot wrote:
Greg gave the voting rule as:
-1 "Veto. And <HERE> is my reasoning."
sorry, I must have missed that post, since I've interpreted the whole thing as: if reduce(operator.add, list_of_votes) > 0 and guido_likes_it(): implement(feature) (probably because I've changed the eff-bot script to use 'sre' instead of 're'...) can you repost the full set of rules? </F>
On Fri, 14 Apr 2000, Fredrik Lundh wrote:
TimBot wrote:
Greg gave the voting rule as:
-1 "Veto. And <HERE> is my reasoning."
sorry, I must have missed that post, since I've interpreted the whole thing as:
if reduce(operator.add, list_of_votes) > 0 and guido_likes_it(): implement(feature)
As in all cases, that "and" should be an "or" :-)
(probably because I've changed the eff-bot script to use 'sre' instead of 're'...)
can you repost the full set of rules?
http://www.python.org/pipermail/python-dev/2000-March/004312.html Cheers, -g -- Greg Stein, http://www.lyra.org/
Fredrik Lundh wrote:
so it's no longer an experimental feature, it's a "static variables" thing?
umm. I had nearly changed my mind to a "okay, if you insist +1", but now it's back to -1 again. maybe in Py3K...
I think that we get 95% of the benefit without any of the "dangers" (though I don't agree with the arguments against) if we allow the attachment of properties only at compile time and disallow mutation of them at runtime. That will allow Spark, EventDOM, multi-lingual docstrings etc., but disallow static variables. I'm not agreeing that using function properties as static variables is a bad thing...I'm just saying that we might be able to agree on a less powerful mechanism and then revisit the more general one in Py3K. Let's not forget that Py3K is going to be a very hard exercise in trying to combine everyone's ideas "all at once". Experience gained now is golden. We should probably be more amenable to "experimental ideas" now -- secure in the knowledge that they can be killed off in Py3K. If we put ideas we are not 100% comfortable with in Py3K we will be stuck with them forever. -- Paul Prescod - ISOGEN Consulting Engineer speaking for himself "Ivory towers are no longer in order. We need ivory networks. Today, sitting quietly and thinking is the world's greatest generator of wealth and prosperity." - http://www.bespoke.org/viridian/print.asp?t=140
I think that we get 95% of the benefit without any of the "dangers" (though I don't agree with the arguments against) if we allow the attachment of properties only at compile time and disallow mutation of them at runtime.
AFAIK, this would be a pretty serious change. The compiler just generates (basically)PyObject_SetAttr() calls. There is no way in the current runtime to differentiate between "compile time" and "runtime" attribute references... If this was done, it would simply be ugly hacks to support what can only be described as unpythonic in the first place! [Unless of course Im missing something...] Mark.
"MH" == Mark Hammond <mhammond@skippinet.com.au> writes:
MH> AFAIK, this would be a pretty serious change. The compiler MH> just generates (basically)PyObject_SetAttr() calls. There is MH> no way in the current runtime to differentiate between MH> "compile time" and "runtime" attribute references... If this MH> was done, it would simply be ugly hacks to support what can MH> only be described as unpythonic in the first place! MH> [Unless of course Im missing something...] You're not missing anything Mark! Remember Python's /other/ motto: "we're all consenting adults here". If you don't wanna mutate your function attrs at runtime... just don't! :) -Barry
Mark Hammond wrote:
AFAIK, this would be a pretty serious change. The compiler just generates (basically)PyObject_SetAttr() calls.
I posted a proposal a few days back that does not use the "." SetAttr syntax and is clearly distinguisable (visually and by the compiler) from runtime property assignment. http://www.python.org/pipermail/python-dev/2000-April/004875.html The response was light but positive... -- Paul Prescod - ISOGEN Consulting Engineer speaking for himself "Ivory towers are no longer in order. We need ivory networks. Today, sitting quietly and thinking is the world's greatest generator of wealth and prosperity." - http://www.bespoke.org/viridian/print.asp?t=140
Paul> I posted a proposal a few days back that does not use the "." Paul> SetAttr syntax and is clearly distinguisable (visually and by the Paul> compiler) from runtime property assignment. Paul> http://www.python.org/pipermail/python-dev/2000-April/004875.html Paul> The response was light but positive... Paul, I have a question. Given the following example from your note: decl {type:"def(myint: int) returns bar", french_doc:"Bonjour", english_doc: "Hello"} def func( myint ): return bar() how is the compiler supposed to associate a particular "decl {...}" with a particular function? Is it just by order in the file? -- Skip Montanaro | http://www.mojam.com/ skip@mojam.com | http://www.musi-cal.com/
On Fri, 14 Apr 2000, Mark Hammond wrote:
I think that we get 95% of the benefit without any of the "dangers" (though I don't agree with the arguments against) if we allow the attachment of properties only at compile time and disallow mutation of them at runtime.
AFAIK, this would be a pretty serious change. The compiler just generates (basically)PyObject_SetAttr() calls. There is no way in the current runtime to differentiate between "compile time" and "runtime" attribute references... If this was done, it would simply be ugly hacks to support what can only be described as unpythonic in the first place!
[Unless of course Im missing something...]
You aren't at all! Paul hit his head, or he is assuming some additional work to allow the compiler to know more. I agree with you: compilation in Python is just code execution; there is no way Python can disallow runtime changes. (from a later note, it appears he is referring to introducing "decl", which I don't think is on the table for 1.6) Cheers, -g -- Greg Stein, http://www.lyra.org/
On Fri, 14 Apr 2000, Fredrik Lundh wrote:
Tim Peters wrote:
[Gordon McMillan]
... Or are you saying that if functions have attributes, people will all of a sudden expect that function locals will have initialized and maintained state?
I expect that they'll expect exactly what happens in JavaScript, which supports function attributes too, and where it's often used as a nicer-than-globals way to get the effect of C-like mutable statics (conceptually) local to the function.
so it's no longer an experimental feature, it's a "static variables" thing?
Don't be so argumentative. Tim suggested a possible use. Not what it really means or how it really works. I look at it as labelling a function with metadata about that function. I use globals or class attrs for "static" data. Cheers, -g -- Greg Stein, http://www.lyra.org/
On Thu, 13 Apr 2000, Vladimir Marangozov wrote:
def this(): ... sucks = "no" ... this.sucks = "yes"
print this.sucks 'yes'
Why on earth 'sucks' is not the object defined in the function's namespace? Who made that deliberate decision? Clearly 'this' defines a new namespace, so it'll be also legitimate to get a NameError, or to:
print this.sucks 'no'
Don't you think?
No.
def this(turing_machine): ... if stops(turing_machine): ... confusing = "yes" ... else: ... confusing = "no" ... print this.confusing <Python kills itself out of mercy>
-- Moshe Zadka <mzadka@geocities.com>. http://www.oreilly.com/news/prescod_0300.html http://www.linux.org.il -- we put the penguin in .com
participants (17)
-
Andrew M. Kuchling
-
Barry A. Warsaw
-
bwarsaw@cnri.reston.va.us
-
Fred L. Drake, Jr.
-
Fredrik Lundh
-
Fredrik Lundh
-
Gordon McMillan
-
Greg Stein
-
Jeremy Hylton
-
Ken Manheimer
-
M.-A. Lemburg
-
Mark Hammond
-
Moshe Zadka
-
Paul Prescod
-
Skip Montanaro
-
Tim Peters
-
Vladimir.Marangozov@inrialpes.fr