When I define functions, in what order should I put the parameters?
I often find it hard to decide and am inconsistent. Are there some rules I could follow开发者_如何学运维?
For example:
def remove_except(haystack, needle, exclude_value):
for key in hackstack:
if key in needle and haystack[key] != exclude_value:
hackstack.pop(key)
could easily be:
def remove_except(needle, exclude_value, haystack):
for key in hackstack:
if key in needle and haystack[key] != exclude_value:
hackstack.pop(key)
Not a perfect example because I tend to put the variable being modified as the first parameter, e.g. haystack, and that might even be a convention but apart from that I'm unsure what to do.
I'm interested for more languages than just Python.
The only rule is be consistent. That doesn't only mean be consistent between and within the APIs you define, but also be consistent with any APIs commonly used in the environment. Note that this may mean you do things differently in one environment than in another. Try to fit in.
Now, with Python and JavaScript, you can use object orientation to take at least the haystack out of the question, because the haystack will be (or at least, can be) an object you're acting on and so how you do that will be dictated by the language (e.g., delete haystack[needle];
[JavaScript for removing a property from an object] or del haystack[needle]
[Python for removing a dictionary entry]). C isn't object-oriented (C++, D, Objective-C, and several other derivatives are) and so you'll need to look for the convention there.
Subjectively speaking, in my experience, the haystack
normally comes first, but that may just be my experience. Similarly, my subjective opinion would be that the exclude_value
would be the last thing (so, haystack
, needle
, exclude_value
) because there would be other operations that would take haystack
, needle
, something_else
— but that's just an opinion.
I believe the convention is, left to right, most vital to least vital. so for your example I would do (needle, haystack, exclude_value). needle is the most vital in this case because its the defining info of the call. the same haystack could be used in another call with a different needle. Exclude_value seems like it could be optional.
It is very much up to you. If you are writing a single function that is unlike anything else in the containing, say, project then required/important parameters go first and optional/unimportant trail after. The language you are using may influence choices; Python allows optional named parameters or keyword arguments to be supplied but they have to follow positional parameters.
If you are writing a set of similar functions for an API then consistency becomes more important. Using the haystack example, you might have
def insert(haystack, needle)
def remove(haystack, needle)
def remove_except(haystack, needle, exclude_value)
def copy(haystack, haystack2)
where haystack goes first because it is the key parameter for all the listed methods and having
def remove(needle, haystack)
def remove_except(haystack, needle, exclude_value)
would look odd (but of course would still work).
If you using an object-oriented language and you find yourself writing a whole slew of functions that share a parameter, you probably consider making that an object and the functions methods that operate on that object but that's probably outside the scope of the question.
For the language of your choice, find some well-used code and study it. If you are using Python, take a look in the standard libraries. For Javascript, jQuery might be good.
Just as some supporting info for the chorus of "put the haystack first", in this case mostly for JavaScript: when you're thinking about which parameter is the "most important", or which is the "haystack", it (can) help to think about which parameter is likely to be different between invocations, and which is likely to change. In your example, the "needle" is probably pretty volatile from invocation to invocation, while the "haystack" may always, or almost always, be the same thing.
Well if that's the case — that it's common to use a utility like that for just one particular object — the "bind()" method available in new JavaScript environments (on the Function prototype), or added by a library like Prototype or Functional, can be really useful:
function whatever(haystack, needle) {
// ...
};
var myHaystack = new Haystack( /* ... */ );
var myHaystackWhatever = whatever.bind(null, myHaystack);
Now the function "myHaystackWhatever" can be used like this:
var result = myHaystackWhatever(someNeedle);
That may seem trivial, but in a functional language like JavaScript the ability to easily and reliably create a new function as a variation on an existing one is super handy, saving a lot of typing and paren/brace matching messiness.
The point here is that facilities like "bind", especially in JavaScript which has purely order-based binding of invocation values to formal parameters, you bind in fixed values from left to right. Thus, if your left-most parameter is the one most likely to be a zillion different things, then "bind()" isn't too interesting. (The Functional JavaScript library has a way of binding other parameters and not just left-to-right, but it's pretty exotic and weird and, in my opinion, mostly interesting as a coding example.)
I believe that's entirely up to you.
In your example, I would start with the object being operated on or performing the action. Then the most necessary parameters first. So Haystack, needle, excluded value. If there is a possibility of having another version of the function with some parameters dropped (no excluded value for example) these typically go at the end. I am thinking in C right now, but your example is python, so there is the possibility of handling optional or default parameters...
Aside from the "informal" rules of consistency or "most-important-to-least-important", there are also some language rules to be observed in Python:
A function definition can contain (in that order):
- positional arguments (i. e. required parameters without a default value),
- keyword arguments (i. e. optional arguments where a default value is specified during function definition),
- variable arguments (i. e. an unspecified number of unnamed parameters), and/or
- variable keyword arguments (i. e. an unspecified number of freely "nameable" keyword arguments)
So you can write
def myfunc(x, y, z=0, truncate=True, *args, **kwargs):
do_something()
which could then be called
myfunc(1,2)
myfunc(1,2,3,False)
myfunc(1,2,3,True, "some more args", mykeyword="keyword arg")
But you can't define a function like this:
def wontwork(x=0, y, z):
def wontworkeither(*args, debug=True)
This is covered in detail in the Python Tutorial.
精彩评论