Maia McCormick

Programmer, writer, nerd

My name is Maia. I’m a programmer based in New York City, currently working as a software engineer at /windmill. This is where I (somewhat infrequently) chronicle my journeys through code, as well as other vaguely related adventures.

Lessons in Default Function Arguments

Today on “Bugs that everyone already knew about but Maia found out about for the first time so will write up anyway”, we’re going to talk about the perils and pitfalls of using mutable objects as default arguments for functions in Python. (This episode brought to you by Maia’s contradance database and the letter Y.)

No, nothing went wrong in my code that caused me to learn this lesson, but multiple code reviewers raised red flags about this line in my code: def resolve_query_dict(d, moves_list=Move.objects.all()). Move.objects.all(), by the way, is a fancy Django function that returns a list of all of the Move objects in your database, and since I would likely never be running this code over an extended period of time while things were added to the database, accidentally freezing the value of “all of the moves in my database” wouldn’t have really been an issue, but the dangers of writing code like the above are still whacky and interesting. Basically, odd things happen when you use mutable objects or called functions as default variables in your functions.

Javascript/JQuery 101

Funnily enough, when you’re teaching yourself how to do stuff pretty much from scratch, sans tutorials or guidance, you miss a few crucial points. Weird, right? Here (mostly for my own records, but you can read it if you want!) are the things I learned this afternoon from some code review of my Javascript/JQuery/Datatables code for my contra database:

JS != JQuery!!! They’re different things! They each have different sets of methods, and you need to keep track of whether any given thing you’re dealing with in web scripting is a JQuery or DOM (and therefore JS) object.

So Much for That Project

Alas, it seems that my dreams of writing a chorale harmonizer in the style of J.S. Bach have to be put to bed until another day. Susan and I have been taking various stabs at this, trying out a few ineffective Python midi programs (one of these, incidentally, had ZERO documentation and was THE MOST FRUSTRATING THING EVER) before settling on mingus, a midi/music theory Python library. Unfortunately, it seems like no one’s addressed any bug reports since 2011, so when we realized that midi read-in was totally borked, we were a little stymied. Specifically, if we gave it this file as input:

Input file

It returned this mess here:

Output file

Asking for TOO MUCH Help?

(Apparently some people at Hacker School think I’m an Iron Blogger, despite me being nothing of the sort. There’s no money riding on whether or not I make consistent blog posts—but I figure I should do what I can to sustain this favorable impression of me!)

Since my HS halfway check-in, I’ve resolved to ask more questions and not let myself get stuck banging my head against problems that could be solved in a hot second by any of the three people sitting next to me. I think that I’ve done a pretty good job with this resolution; I ask for help way more frequently, and as a result, I think I get more done. However, I feel the need to remind myself that there is such a thing as asking for too much help.

Take this example: I was sitting down with Tristan, a fellow HSer, attempting to set up virtualenv and get all of my permissions and installs un-borked. I was totally in over my head, having never used virtualenv before, and he was helping me sort it out. There was some funky stuff that needed fixing having to do with my $PATH being invalid because one of the folder names contained a space. We dealt with that issue, and now most of the stuff worked, but I still couldn’t manage to run bpython from inside my virtualenv. I showed Tristan the error message my computer was spewing, and asked him, “What’s going on?”

His response: “What do you think is going on?”

Me: “Well, it won’t run bpython!”

Him: “Come on, you can do better than that.”

Observations From Phonebook Code Review

Here are some gleanings from Amy’s code review of my phonebook applet, and some self-study. Mostly for my own records and so I actually absorb all of this by writing out, but if anyone has comments, additions, disagreements with anything I’ve written here, fire away!

  • misc. layout stuff, like putting import statements in alphabetical order
  • avoid global vars when possible. I already knew this, but it’s good to remember. For example, if you’ve assuming your program will be run on the command line, you can put global vars in the main() function.
  • functions that only incidentally return stuff are bad news. I had a function called phonebook_exists that checked to see if the phonebook you were trying to look up existed; if it didn’t exist, the func. would throw an error, and if it did, it would return the data of the phonebook. Which in retrospect was super weird because nothing the function name indicated that it was what you should use to get the contents of a saved phonebook. A much more sensible way to do this is to have a function called read_phonebook which attempts to return the contents of the saved phonebook, but if it fails (which you figure out with a try/except), then it throws an error. (Incidentally, I had a similar confusion with when things should and shouldn’t be returned in another project I’m working on, my bootleg homemade git. I had a function called save_at_hash that would hash a file, save it at its hash, and then return the hash. Why did this function return the hash? I assumed it would be useful. Couldn’t really tell you why. I ended up refactoring it into two functions, make_hash—which took a file and returned a hash—and save_at_hash—which took a file and a hash and saved a copy of the file at that hash.)

Taming the Octopress

I was messing around with my website yesterday, and trying to make two sub-websites for my ‘Music’ and ‘Programming’ hats in a single Octopress install, and I really was going to be on time to my dinner plans except once my website experiment was borked and I tried to restore my previous site… well, long story short, it was the borked-est, and I spent an hour then and an hour today hunting around with Allison trying to fix the problem. Turns out that in trying to roll back my changes, I’d introduced some discrepancy between my master and gh-pages branches, and I could get around all of this on github with force pushes (-f), but not so easily with actually deploying my site (rake deploy). In the end, I had to go into my Rakefile and add a plus sign somewhere that tells Octopress not to worry whether it’s doing fast-forward commits1 and just commit anyway.

All About Git

Here are the fruits of a mini-seminar on git from the inimitable Mary—a sketchy portrait of the inner workings of git.

Git: The Bestest Version Control System with the Worstest UI

All of your Git-age lives in the .git folder

When you run git init, all it does is create a .git folder. That’s it! Your machine knows what folders are and aren’t git repos by the presence or absence of a .git directory. .git is a hidden folder, so to see it, type ls -a. Whenever you add or commit things to your git repo, they’ll get stored in here.

Nested Functions

Know what’s cool? Nested functions.

The other day, as I was making this craaaazy dance parser (that’s getting more and more convoluted by the minute), Alan suggested a really baller way of organizing it: using a bunch of little mini-parser functions that all look for something specific in your text, and smooshing related parsers together into one macro-parser that runs all of them in turn until one returns something. In Alan’s lovely mock-up code that I’m shamelessly copying over here, that might look like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def contains_an_a(input):
    return input.find("a") > -1

def contains_a_b(input):
    return input.find("b") > -1

def one_of(parsers):
    def parser(input):
        for p in parsers:
            result = p(input)
            if result:
                return result
    return parser

contains_an_a_or_a_b = one_of([contains_an_a, contains_a_b])

The handy thing about having all of these bite-size functions is that none of them gets too crazy bulky, and also, since they’re functions (and not dicts like I was using earlier), I can use regexes in them! I’m also jamming on the idea of passing functions to other functions, so I made an even bigger function called use_parser that takes a parser, a default value, and an ‘ask’, which it uses to ask the user (via raw_input) what the value should be. This way, I can take a single parser and customize it in a variety of ways. Say for example I have a distance parser: for do-si-dos and gypsies it should default to False (because those moves don’t necessarily need to take a distance) and for allemandes it should ask the user, “what’s the value of ‘dist’ here?”

Slicing Is Not Enough (or, Adventures in Deep Copy)

The sudoku solver that I’m working on with Miriam was nearly finished before it started when we were playing around with ways to draw a board to terminal. I came to the table with some initial code I had written—

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
def draw_board(board):
    for i in range(0,9):
        board[i].insert(6, "|")
        board[i].insert(3, "|")
        row_string = "  ".join(map(str, board[i]))
        if i in [2, 5]:
            print row_string
            print "________________________________"
        else:
            print row_string

def newboard():
    return [[0,0,0,0,0,0,0,0,0],
        [0,0,0,0,0,0,0,0,0],
        [0,0,0,0,0,0,0,0,0],
        [0,0,0,0,0,0,0,0,0],
        [0,0,0,0,0,0,0,0,0],
        [0,0,0,0,0,0,0,0,0],
        [0,0,0,0,0,0,0,0,0],
        [0,0,0,0,0,0,0,0,0],
        [0,0,0,0,0,0,0,0,0]]

and the shiny output!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
>>> myboard = newboard()
>>> draw_board(myboard)

0  0  0  |  0  0  0  |  0  0  0
0  0  0  |  0  0  0  |  0  0  0
0  0  0  |  0  0  0  |  0  0  0
________________________________
0  0  0  |  0  0  0  |  0  0  0
0  0  0  |  0  0  0  |  0  0  0
0  0  0  |  0  0  0  |  0  0  0
________________________________
0  0  0  |  0  0  0  |  0  0  0
0  0  0  |  0  0  0  |  0  0  0
0  0  0  |  0  0  0  |  0  0  0

Everything was awesome! Or so I thought. But look what happened when I ran the code again.

Checking in With the Hacker School Experience

Oh hai there, blag! It’s been a while. Too long. The days here blur together occasionally—hours of staring a screen working on multiple only-slightly-different problems will do that to you, and it’s really easy to lose track of what you’ve been working on, accomplishing, learning, and thinking about. Especially when those accomplishments seem really tiny and mundane. (“Yay, I got my database search page to work! Yay, I got my database search page to work for two things in sequence! Wait, it’s 7:00?”)

Had a one-on-one with Allison this afternoon about the State of the Hack so far—how HS is treating me, what I might want to do differently, what I might want to be different about the environment. Here are some thoughts prompted by that conversation.

  • I want to get better at asking for help, surmounting impostor syndrome, etc. I’m pretty good at this, all things considered, but I still need to do lots more work on this front. Sometimes asking a question on a Zulip thread isn’t what I need—it happens to not get responded to, or (more often) I would benefit much more from discussing it in person.
  • An interesting thing about the HS environment is how it’s way more normal to Zulip someone from across the room than go over and tap them on the shoulder. Generally, I get way more out of an in-person conversation than an online one, so I’d like to be better about just finding people in person to ask them questions. (Of course, the benefit of Zulip-ing is that you know who can/can’t be interrupted. Tricky balance here. But I should feel more able to approach facilitators, at least.)