why does menu_func() in this example reference a self when it is declared outside of a class
The answer lies in what happens in the register function, below the menu_func:

def register():
    bpy.utils.register_class(AddBox)
    bpy.types.INFO_MT_mesh_add.append(menu_func) # &lt;&lt;&lt; This is the interesting bit


As you can see here, the menu_func is appended to the INFO_MT_mesh_add type as a new method. INFO_MT_mesh_add is the Add Mesh Menu in the blender UI.

This is the class where you will be adding the menu_func to, not the add_box operator.

This function is added to the Add Mesh Menu, and when you call it from the blender UI, it will be executed like any method. That means that it will get the class instance as the function's first parameter, "self".

The word "self" has no real meaning by itself, and is merely the first parameter this function receives. It is a convention in python (and some other languages) to call it "self", but you could call it "james" and it would work just fine.

When the function is a method in a class, it automatically gets the instance of the class as the first parameter when called from the instance (or the class itself if its a @classmethod, in which case we usually call the first parameter cls instead of self).

class someClass:
    def myMethod( self ):
        print( "I get the class instance as my first param 'self'" )
        print( self )

clsInstance = someClass()
clsInstance.myMethod() # myMethod will get clsInstance as the first parameter


We could write it this way though, and it would work just the same:

def to_be_method( self ):
    print( "I get the class instance as my first param 'self'" )
    print( self )

class someClass:
    # Reference to_be_method as our class' own myMethod
    myMethod = to_be_method 

clsInstance = someClass()
clsInstance.myMethod() # myMethod will get clsInstance as the first parameter


So as you can see in the example above, we define a function outside the class and used the "self" parameter, and then referenced this function inside the class.

Appending the menu_func into the INFO_MT_mesh_add class does something similar, which is why it's fine and works well.

I hope this explains the situation rather than confuse you even more! :)