View in #code_tips_and_tricks on Slack
@ozzmeister00: Sooooooooo, I ran into something cool about Python today. init is called after the methods of a class are evaluated. So for example:
def __init__(self, val):
self.variable = val
def variable(self):
return 'Pong'
>>> a = Thing('Ping')
>>> a.variable
'Ping'
>>> a.variable()
Traceback (most recent call last):
File "<pyshell#8>", line 1, in <module>
a.variable()
TypeError: 'str' object is not callable
@ddevoe: “cool” or cool?
@passerby: well init can call other methods
@bob.w: Yeah, the class body is evaluated when the file is loaded at ‘compile time’. It can be altered by using a metaclass or a class decorator.
@jeff_hanna: How can we use this knowledge for evil?
@theodox: it has to work like this, so that __init__
can call other class or instance methods. Otherwise you’d have to control it by moving the location of __init__
around in the file
If you need to catch it at creation time is to override __new__
@bob.w: __new__
already has the class body set. You need to get at it earlier than that if you want to mess with it.
@theodox: yes, but __new__
precedes __init__
so you could change what __init__
has to work with
@bob.w: Very true.
That also operates on a per instance level. So you’d be doing the alteration each time a new instance is created, which could be what you want.
Most of the times I’ve wanted to mess with the class scope, I’ve wanted to do it for all instances, which is probably why my brain went to metaclasses and class decorators first.
in python3 you can even override the __build_class__
function to mess with class creation much like overriding __import__
can be used to change how the import
statement operates.
@theodox:
class X(object):
def __init__(self, start):
self._inner = start or self._inner
def val(self):
return self._inner
class Y(X):
def __new__(cls, *args):
result = X.__new__(cls, *args)
result._inner = 999
return result
hello = Y(None)
print hello.val()
if you wanted to do something like recycle ids or timestamp objects you could do it there.
It’s goofy but it works
messing with __new__
is like the super-minimal version of metaclassing
the only time I’ve every done it for realz is creating classes that inherit from string
@bob.w: tuple
uses __new__
as well. frozenset
probably does as well, but I’ve never tried to subclass that one
the immutable builtins rely on __new__
to do most of their initialization
@theodox: Python: Too Clever By Half™
@bob.w: Python: “You can do anything, but really should you?”
@adam.pletcher: I fucked with our import mechanics at work once and didn’t tell anyone about it. Might even still be in place, I’m not even sure, lol.
@passerby: i feel like every language has a few features where if you use them you should be questioning why
@covinator: Like using strings and MemberInfos in C# to get the value of fields via reflection.
weeps slowly in the corner
@passerby: exactly
though when working with some more limited api;s like unity reflection can save your ass at times
but 99% of the time if you think reflection is the answer you fucked up