On Mar 18, 8:21 am, "Dustan" <[EMAIL PROTECTED]> wrote: > On Mar 18, 7:25 am, "Dustan" <[EMAIL PROTECTED]> wrote: > > > > > On Mar 18, 7:06 am, Steven D'Aprano > > > First, an example of the code in action. > > > > >>> import PrivateAttributes > > > >>> obj = PrivateAttributes.TestPrivateClassAttributes() > > > >>> obj.getNumInstances() > > > 1 > > > >>> another = PrivateAttributes.TestPrivateClassAttributes() > > > >>> obj.getNumInstances() > > > 2 > > > >>> athird = PrivateAttributes.TestPrivateClassAttributes() > > > >>> athird.getNumInstances() > > > > 3 > > > > The getNumInstances method reports the number of instances of the > > > PrivateAttributes class. There's no obvious class attribute where this > > > count is being kept: > > > > >>> obj.__class__.__dict__.keys() > > > > ['__module__', 'getNumInstances', '__dict__', '__weakref__', '__doc__', > > > '__init__'] > > > > Here's how to hack it, and make it report wrong numbers. > > > > >>> c = obj.getNumInstances.func_closure > > > >>> c[1].cell_contents.numInstances = -300 > > > > >>> athird.getNumInstances() > > > -300 > > > >>> afourth = PrivateAttributes.TestPrivateClassAttributes() > > > >>> athird.getNumInstances() > > > > -299 > > > > So yes, it is absolutely hackable. > > > I did have a feeling that it was hackable, but had no idea how one > > could possibly go about hacking it (I was starting to wonder of there > > was a way to apply locals() and globals() on functions). But now I > > (ehem) sorta know how it's done. > > > > Now, I'm hardly a Python guru, but in about fifteen minutes I followed the > > > trail through the object chain, and found how to hack this. An real guru > > > would probably do it in three minutes. > > > > I was helped a bit by having the source code. But even without the source > > > code, I reckon I could have done it in an hour or so, if I was motivated > > > enough. All the tools you need are a Python interactive session, the dir() > > > function and the dis module. > > > I have used all of those before, but I haven't been able to fully > > understand the output of the dis module; maybe that's my problem. > > Alright, perhaps you can help me out with this learning curve here. I > have seen, but not worked with, some basic assembly code, so I think I > have a vague idea of what it all means, although I have a feeling it's > not all valid assembly (on any widely used machine). > > First I dis.dis'd testPrivateStaticFunctionVariables: > > >>> dis.dis(testPrivateStaticFunctionVariables) > > 21 0 LOAD_DEREF 0 (func) > 3 LOAD_DEREF 1 (internalData) > 6 LOAD_FAST 0 (args) > 9 CALL_FUNCTION_VAR 1 > 12 RETURN_VALUE > > At first I was a little confused by this, because there's no increment > in sight, but then I realized it was dis.dis'ing the wrapper closure > in the internalDataDecorator closure in the PrivateDataEngine function > (and then I hit myself on the head and cried out "doh!"). So that > 'code' is coming from this (taken out of closure): > > def wrapper(*args): > return func(internalData, *args) > > So, based on what you showed me, I found my way after some failed > tries to this: > > >>> dis.dis(testPrivateStaticFunctionVariables.func_closure[0].cell_contents) > > 28 0 LOAD_FAST 0 (internalData) > 3 DUP_TOP > 4 LOAD_ATTR 0 (numCalls) > 7 LOAD_CONST 1 (1) > 10 INPLACE_ADD > 11 ROT_TWO > 12 STORE_ATTR 0 (numCalls) > > 29 15 LOAD_FAST 0 (internalData) > 18 LOAD_ATTR 0 (numCalls) > 21 RETURN_VALUE > > That's coming from this: > > @PrivateDataEngine(numCalls = 0) > def testPrivateStaticFunctionVariables(internalData): > """returns the number of times this function has been called.""" > internalData.numCalls += 1 > return internalData.numCalls > > Here's a few questions on this output, for which I would highly > appreciate some answers: > > What's the difference between 'LOAD_DEREF', 'LOAD_FAST', and > 'LOAD_CONST', and, as seen athttp://docs.python.org/lib/module-dis.html, > 'LOAD_GLOBAL'? I can imagine that 'LOAD_GLOBAL' loads a global, but > seeing as python is such a dynamic language, how exactly is it > supposed to distinguish between them? > > I don't understand the following at all: 'DUP_TOP', 'ROT_TWO'. Any > pointers? > > What does 'INPLACE_ADD' mean, if not in place addition, and if it is > in place addition, why does it need to 'STORE_ATTR' afterward? > > Thanks for any help!
If this had been on any other usenet group, someone would have RTFM'd me to hell. http://docs.python.org/lib/bytecodes.html I might still have some more questions later, but for now, I'll look at this. > > > -- > > > Steven -- http://mail.python.org/mailman/listinfo/python-list