On Tue, Jul 5, 2016 at 4:33 PM, Steven D'Aprano <steve+comp.lang.pyt...@pearwood.info> wrote: > > Got any other tricky questions to add? S P O I L E R
S P A C E A N D A B I T M O R E [Thanks Steven, I just copied and pasted your space. See? You can copy and paste blank space and use it over and over. Could be a useful tip for the people who can't find enough newlines to space out their code or readability.] > Explain metaclasses, descriptors, the mro, multiple inheritance, and the > interaction between them. Why is the mro needed? A metaclass is simply the type of the type of something. By default, that's type, but if you type "metaclass=X" in a class definition, you can make your class a type of something else. (Can I use the word "type" in any other senses? I couldn't work "font" into the above.) Descriptors are objects stored in the class which, when referenced from an instance, return something other than themselves. The most common example is functions; a function in a class becomes a bound method on an instance, thus proving that descriptors make Python better than JavaScript. The MRO is simply the class hierarchy, starting from the current class and ending with 'object', flattened out into a line. In a pure single-inheritance situation, this is just the line of parents; when multiple inheritance comes into play, the MRO becomes more complicated, but still follows straight-forward rules (children before parents, and parents in the order they're listed). The MRO's job is twofold: attributes of classes earlier in the list will shadow those later, and super() means "next in the MRO". > obj.spam is a property. How do you get access to the underlying property > object > itself? Presumably you mean that obj.spam returns the result of calling the property function, something like this: class X: @property def spam(self): return "something" obj = X() In that case, type(obj).spam is the property object, and type(obj).spam.{fget,fset,fdel} are the three functions (of which two will be None in my example). > Why doesn't the property decorator work inside a Python 2 classic class? Guido put an explicit check in to prevent you from doing so. Attempting it will automatically download the latest Python 3.x, point your web browser to a porting tutorial, and raise SyntaxError("Please learn to use Python 3."). Or: Instances of classic classes are all actually instances of Instance(), so the property would have to be attached to Instance. Or: It does. rosuav@sikorsky:~$ python2 Python 2.7.11+ (default, Jun 2 2016, 19:34:15) [GCC 5.3.1 20160528] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> class X: ... @property ... def spam(self): ... print("Spamming") ... return "Hello"*5 ... >>> x=X() >>> x <__main__.X instance at 0x7f06e44fad88> >>> x.spam Spamming 'HelloHelloHelloHelloHello' Take your pick. > Explain Python scoping rules, in particular with respect to nested classes and > nested functions (or even nested classes inside nested functions). If you have an explicit global or nonlocal directive, the listed name(s) are looked up in that scope only. Otherwise, Python looks in local names, then in enclosing function scopes, then the module globals, and finally the built-ins. While executing a class or function body, local scope is that class/function. Enclosing class scopes are not searched. > Explain attribute lookup rules. When does an instance attribute take priority > over a class attribute? With simple attributes, an instance attribute always takes priority, and then class attributes in method resolution order. With descriptors... I'd have to go look it up. I've never done any shadowing of descriptors, at least not deliberately/consciously. > When is locals() writable? When is locals() writable, AND the effect shows up > in the local scope? Explain how exec works inside a function, and the > interaction with locals(). In CPython, locals() is always writable. It's just a dictionary. I'm not sure whether this is a language guarantee or not. When does the effect show up in local scope? When locals() is globals(). Otherwise, the answer has to be "undefined" or at best "implementation defined". To go further than that, I have to actually experiment, rather than going from memory. rosuav@sikorsky:~$ python3 Python 3.6.0a2+ (default:57f3514564f6, Jun 29 2016, 16:27:34) [GCC 5.3.1 20160528] on linux Type "help", "copyright", "credits" or "license" for more information. >>> def f(): ... x = 1 ... print(id(locals()), id(globals())) ... locals()["x"] = 2 ... print(x) ... >>> f() 140191368813960 140191369099208 1 >>> Same is true of CPython 2.7, PyPy 5.1 (including PyPy.js), Jython 2.5, MicroPython (although this has its own bizarrenesses - locals() is globals(), somehow), but NOT of Brython. In Brython, locals and globals have distinct ids, and mutating locals *does* change the local name binding. And this is legal, according to the docs. End of experimentation, now back to doing stuff from memory. In Python 3, I don't think exec() can interact with locals() in any way. It's a perfectly ordinary function; you can give it a reference to locals() if you like, but it's no different from other mutations of locals. In Python 2... I don't use exec inside functions. So I'd have to look it up or experiment. But I'd rather just stick to Py3. > Name all the falsey builtins. Fred, Joe, Stanley, Margaret, and Louise. I don't think there are actually any falsey builtins. Most builtins are either classes (object types like int/str/range, and the exceptions) or functions (next, ascii, eval). None and False are keywords, so they don't count. Literals like (), 0, 0.0, 0.0j, u"", b"" aren't builtins either. And [] and {} aren't literals OR builtins, they're special forms of constructor that return falsey objects. > Apart from exceptions, list the builtins, from memory. You can list the > exceptions as well. Oh great. Uhh... there's a lot of them. int, bool, str, bytes, list, tuple, set, dict, object, type, float, complex, next, ascii, eval, exec, range, sorted, super, zip, map, enumerate, len, iter, repr, locals, globals, id... I'm stopping there. > An easy one: list the Python keywords. Not that easy actually. I'm far from sure that I got them all. True, False, None, if, elif, else, for, while, break, continue, try, except, finally, raise, with, as, async, def, return, and, or, not, is, in, class, pass, yield, global, nonlocal, yield, from, import How many have I missed? > What happens in this code snippet? > > L = [1] > t = (L,) > t[0] += 1 > > Explain what value t has, and why. Correction from other post: Last line becomes "t[0] += [1]" t has the same value it had before - the same tuple. That tuple references L, which has just been extended and is now [1, 1]. So if you print out t, you'll see ([1, 1],) - but it's still the same value it was. Also, you'll have seen a TypeError at the moment when you attempted to mutate the tuple, which is a violation of the Guildpact, subsection... whatever. The same one that says that coffee is an acceptable substitute for rest for the Living Guildpact. > Explain what "yield from it" does and how it is different from: > > for item in it: > yield item If 'it' is another generator, 'yield from' also passes along any values sent, or any exception thrown in. With non-generator iterators, I believe it's functionally identical to the above for loop. ChrisA -- https://mail.python.org/mailman/listinfo/python-list