Starting a long blog post…
Posts tagged python
Stop Writing Classes. I totally agree.
I tend to develop around abstractions. In Python, this tendency manifests itself as multiple Python packages.
As I develop them, I find I need to play around with my
$PYTHONPATH. But I can’t stand typing a long command over and over, so I put together two aliases that I use all the time.
alias pythisdir='export PYTHONPATH=$PWD'
I use this command basically to support building tests or building demos. When I’m not sure what I’m trying to build, but I have rough ideas, I start writing demos. I think the code should look like this. And then I make it true.
This looks like having a
project_dir/demos/ directory. Working on a project, then, looks roughly like this:
$ cd ~/Projects/ $ pythisdir $ cd demos $
The next alias is
triforce. I use this when I’m working on Brubeck. The things I’m doing in Brubeck often feed back into DictShield modifications, so I will setup a virtualenv with everything I need, except for Brubeck and DictShield, and I’ll add them via this alias.
alias triforce='export PYTHONPATH=$PWD:$HOME/Projects/dictshield:$HOME/Projects/brubeck'
The alias also includes the current directory, in case I’m building a site in Brubeck. Sites themselves inform Brubeck design decisions, which inform DictShield design decisions.
I really enjoy when abstractions teach other new ideas. It’s like a few little identities all figuring out how to be compatible and efficient. Hello, Mr. Brubeck! And great to see you, Mr. DictShield! Are you ready to jam on some Readify?
Feels like a milestone.
Disclaimer: I can’t tell if I like this behavior or not, but I’m leaning towards it being an awesome way to avoid using classes while still gaining the benefit of singleton-like behavior.
def init_something(): if not hasattr(init_something, 'val'): val = 'WHATEVER' init_something.val = val return init_something.val
Let’s dig into what this code is doing.
How It Works
First, notice that we are using the function’s name,
init_something inside it’s definition. I can then use this name to check if any attributes are attached to it. Being able to reference a function by it’s name is similar to using
self in a class, except we won’t be using a class.
Next, we’ll call our cached value
val. You probably want a more descriptive name in practice, but this works for now. If
init_something hasn’t initialized
val, we will give it a value and then attach it to the function. Every check after the first one will already have
val ready and waiting to go.
So… does it work? Let’s add a print statement and see.
>>> def init_something(): ... if not hasattr(init_something, 'val'): ... val = 'WHATEVER' ... print 'Initializing value' ... init_something.val = val ... return init_something.val ... >>> init_something() Initializing value 'WHATEVER' >>> init_something() 'WHATEVER' >>> init_something() 'WHATEVER'
Sweet! We only see it initialize the value on the first call
Doing It With Classes
I find it neat because the alternative is to use a class, which is heavier. This same behavior is achieved by using a class and implementing `__new__()`. This is different from `__init__()` because `__new__()` is the function responsible for creating `self`, which gets passed into `__init__()`.
That means we can implement `__new__()` such that it will only create a new instance in the case that one doesn’t already exist. That looks like below.
>>> class SomeClass(object): ... def __new__(cls, *a, **kw): ... if not hasattr(cls, 'val'): ... print 'Initializing value' ... cls.val = 'WHATEVER' ... return cls.val ... >>> sc = SomeClass() Initializing value >>> sc = SomeClass() >>> sc = SomeClass()
Notice that using it even seems to behave the same. The only difference is that one is a function and one is class and when I have to choose, I will choose the function.
Check out this clever poem I got from @workmajj for my brrffday.
#!/usr/bin/python def wrap(fn): def box(): print "Happy %s, dude!" % (fn()) return box @wrap def gift(): return "birthday" gift()