When is it required to define a separate function in Python - python

In my code, I am printing a menu
print(num_chars * char)
for option in options:
print("{:d}. {:s}".format(option, options[option]))
print(num_chars * char)
The code print(num_chars * char) prints a separator/delimiter in order to "beautify" the output. I have learned from several coding tutorials that I am not allowed to write the same code more than once.
Is it really preferable to define a function
def get_char_repeated(char='*', num_chars=30):
"""
Return the character repeated an arbitrary number of times
"""
return num_chars * char
and call this two times in my original code?
Are there any alternatives if I need to print nice looking menu from a dictionary?
Thank you.

I have learned from several coding tutorials that I am not allowed to write the same code more than once.
This principle, called "don't repeat yourself" (DRY) is a good rough guideline. For every programmer who writes too many functions (splitting code into too small units), there are 20 who write too few.
Don't go overboard with it, though. The reasoning behind DRY is to make reading and changing the code later on easier. print(num_chars * char) is pretty basic already, and super-easy to understand and change, so it doesn't really pay off to factor it into a function.
If the repeated code grows to 3 lines, you can (and probably should) factor it out then.

It's not necessary at that level. What might be helpful is if you were often doing that whole block of code, you could easily change it to
def printOptions(options, char='*', num_chars=30):
print(num_chars * char)
for option in options:
print("{:d}. {:s}".format(option, options[option]))
print(num_chars * char)
The main point of functions is to save time with blocks of code you use a lot in a very similar way by not retyping/copy pasting them. But they also save time when you make changes. If you used the same function in 10 different places you still only need to change it once for all 10 uses to be updated rather than having to find all 10 manually and update them.
So if you decided you wanted to put a title header into this menu printing section and you had used it as this or a similar function in a bunch of places, you could quite easily update them all without difficulty.

I find it a good rule, that if the code you're turning into a function is used more than once and takes up more than 3 lines, it is a candidate to be turned into a function. If it is a very complex single line of code (like this x = [ i**j for i in z for j in y ]) and is used more than twice, it could be a candidate to turn into a function.
It may be a matter of preference where you draw the line but the basic idea is if it makes your code easier to read or easier to write, turning something into a function can be a good idea. If it makes your code harder to read (because every time you see the function you have to look back at the specifics of what it does), you probably should not have turned that code into a function.

Related

call a variable from a string

I am working on a python hangman game and have my graphics based on a variable, lives, so when you have 1 lives, the corresponding picture appears. i put my graphics into definitions and want to call them from that. Here's my code so far:
if life== 1:
graphics1()
if life==2:
graphics2()
if life==3:
graphics3()
if life==4:
graphics4()
if life==5:
graphics5()
if life==6:
graphics6()
if life==7:
graphics7()
if life==8:
graphics8()
if life==9:
graphics9()
if life==10:
graphics10()
and so on, so is there a command which would allow me to shorten this to one or two lines?
I highly discourage it, but it could be accomplished by doing:
globals()["graphics" + str(life)]()
However there's most definitely a better solution. Presuming that the contents of each graphics*() is similar to one another.
Well, multiple if branches can be expressed via lookup table as
lt = {"1":graphics1(), "2":graphics2()}
and so on.
Accès to if branch via (and provide a fallback function)
lt.get(life, graphics1())
If you want to create the dict dynamically use the dict comprehension and use the getattr to get the function.
Or, as stated above, generalize the function graphics passing the "ith" param to it.

Docstrings vs Comments

I'm a bit confused over the difference between docstrings and comments in python.
In my class my teacher introduced something known as a 'design recipe', a set of steps that will supposedly help us students plot and organize our coding better in Python. From what I understand, the below is an example of the steps we follow - this so call design recipe (the stuff in the quotations):
def term_work_mark(a0_mark, a1_mark, a2_mark, ex_mark, midterm_mark):
''' (float, float, float, float, float) -> float
Takes your marks on a0_mark, a1_mark, a2_mark, ex_mark and midterm_mark,
calculates their respective weight contributions and sums these
contributions to deliver your overall term mark out of a maximum of 55 (This
is because the exam mark is not taken account of in this function)
>>>term_work_mark(5, 5, 5, 5, 5)
11.8
>>>term_work_mark(0, 0, 0, 0, 0)
0.0
'''
a0_component = contribution(a0_mark, a0_max_mark, a0_weight)
a1_component = contribution(a1_mark, a1_max_mark, a1_weight)
a2_component = contribution(a2_mark, a2_max_mark, a2_weight)
ex_component = contribution(ex_mark, exercises_max_mark,exercises_weight)
mid_component = contribution(midterm_mark, midterm_max_mark, midterm_weight)
return (a0_component + a1_component + a2_component + ex_component +
mid_component)
As far as I understand this is basically a docstring, and in our version of a docstring it must include three things: a description, examples of what your function should do if you enter it in the python shell, and a 'type contract', a section that shows you what types you enter and what types the function will return.
Now this is all good and done, but our assignments require us to also have comments which explain the nature of our functions, using the token '#' symbol.
So, my question is, haven't I already explained what my function will do in the description section of the docstring? What's the point of adding comments if I'll essentially be telling the reader the exact same thing?
It appears your teacher is a fan of How to Design Programs ;)
I'd tackle this as writing for two different audiences who won't always overlap.
First there are the docstrings; these are for people who are going to be using your code without needing or wanting to know how it works. Docstrings can be turned into actual documentation. Consider the official Python documentation - What's available in each library and how to use it, no implementation details (Unless they directly relate to use)
Secondly there are in-code comments; these are to explain what is going on to people (generally you!) who want to extend the code. These will not normally be turned into documentation as they are really about the code itself rather than usage. Now there are about as many opinions on what makes for good comments (or lack thereof) as there are programmers. My personal rules of thumb for adding comments are to explain:
Parts of the code that are necessarily complex. (Optimisation comes to mind)
Workarounds for code you don't have control over, that may otherwise appear illogical
I'll admit to TODOs as well, though I try to keep that to a minimum
Where I've made a choice of a simpler algorithm where a better performing (but more complex) option can go if performance in that section later becomes critical
Since you're coding in an academic setting, and it sounds like your lecturer is going for verbose, I'd say just roll with it. Use code comments to explain how you are doing what you say you are doing in the design recipe.
I believe that it's worth to mention what PEP8 says, I mean, the pure concept.
Docstrings
Conventions for writing good documentation strings (a.k.a. "docstrings") are immortalized in PEP 257.
Write docstrings for all public modules, functions, classes, and methods. Docstrings are not necessary for non-public methods, but you should have a comment that describes what the method does. This comment should appear after the def line.
PEP 257 describes good docstring conventions. Note that most importantly, the """ that ends a multiline docstring should be on a line by itself, e.g.:
"""Return a foobang
Optional plotz says to frobnicate the bizbaz first.
"""
For one liner docstrings, please keep the closing """ on the same line.
Comments
Block comments
Generally apply to some (or all) code that follows them, and are indented to the same level as that code. Each line of a block comment starts with a # and a single space (unless it is indented text inside the comment).
Paragraphs inside a block comment are separated by a line containing a single #.
Inline Comments
Use inline comments sparingly.
An inline comment is a comment on the same line as a statement. Inline comments should be separated by at least two spaces from the statement. They should start with a # and a single space.
Inline comments are unnecessary and in fact distracting if they state the obvious.
Don't do this:
x = x + 1 # Increment x
But sometimes, this is useful:
x = x + 1 # Compensate for border
Reference
https://www.python.org/dev/peps/pep-0008/#documentation-strings
https://www.python.org/dev/peps/pep-0008/#inline-comments
https://www.python.org/dev/peps/pep-0008/#block-comments
https://www.python.org/dev/peps/pep-0257/
First of all, for formatting your posts you can use the help options above the text area you type your post.
And about comments and doc strings, the doc string is there to explain the overall use and basic information of the methods. On the other hand comments are meant to give specific information on blocks or lines, #TODO is used to remind you what you want to do in future, definition of variables and so on. By the way, in IDLE the doc string is shown as a tool tip when you hover over the method's name.
Quoting from this page http://www.pythonforbeginners.com/basics/python-docstrings/
Python documentation strings (or docstrings) provide a convenient way
of associating documentation with Python modules, functions, classes,
and methods.
An object's docsting is defined by including a string constant as the
first statement in the object's definition.
It's specified in source code that is used, like a comment, to
document a specific segment of code.
Unlike conventional source code comments the docstring should describe
what the function does, not how.
All functions should have a docstring
This allows the program to inspect these comments at run time, for
instance as an interactive help system, or as metadata.
Docstrings can be accessed by the __doc__ attribute on objects.
Docstrings can be accessed through a program (__doc__) where as inline comments cannot be accessed.
Interactive help systems like in bpython and IPython can use docstrings to display the docsting during the development. So that you dont have to visit the program everytime.

Is it bad style to reassign long variables as a local abbreviation?

I prefer to use long identifiers to keep my code semantically clear, but in the case of repeated references to the same identifier, I'd like for it to "get out of the way" in the current scope. Take this example in Python:
def define_many_mappings_1(self):
self.define_bidirectional_parameter_mapping("status", "current_status")
self.define_bidirectional_parameter_mapping("id", "unique_id")
self.define_bidirectional_parameter_mapping("location", "coordinates")
#etc...
Let's assume that I really want to stick with this long method name, and that these arguments are always going to be hard-coded.
Implementation 1 feels wrong because most of each line is taken up with a repetition of characters. The lines are also rather long in general, and will exceed 80 characters easily when nested inside of a class definition and/or a try/except block, resulting in ugly line wrapping. Let's try using a for loop:
def define_many_mappings_2(self):
mappings = [("status", "current_status"),
("id", "unique_id"),
("location", "coordinates")]
for mapping in mappings:
self.define_parameter_mapping(*mapping)
I'm going to lump together all similar iterative techniques under the umbrella of Implementation 2, which has the improvement of separating the "unique" arguments from the "repeated" method name. However, I dislike that this has the effect of placing the arguments before the method they're being passed into, which is confusing. I would prefer to retain the "verb followed by direct object" syntax.
I've found myself using the following as a compromise:
def define_many_mappings_3(self):
d = self.define_bidirectional_parameter_mapping
d("status", "current_status")
d("id", "unique_id")
d("location", "coordinates")
In Implementation 3, the long method is aliased by an extremely short "abbreviation" variable. I like this approach because it is immediately recognizable as a set of repeated method calls on first glance while having less redundant characters and much shorter lines. The drawback is the usage of an extremely short and semantically unclear identifier "d".
What is the most readable solution? Is the usage of an "abbreviation variable" acceptable if it is explicitly assigned from an unabbreviated version in the local scope?
itertools to the rescue again! Try using starmap - here's a simple demo:
list(itertools.starmap(min,[(1,2),(2,2),(3,2)]))
prints
[1,2,2]
starmap is a generator, so to actually invoke the methods, you have to consume the generator with a list.
import itertools
def define_many_mappings_4(self):
list(itertools.starmap(
self.define_parameter_mapping,
[
("status", "current_status"),
("id", "unique_id"),
("location", "coordinates"),
] ))
Normally I'm not a fan of using a dummy list construction to invoke a sequence of functions, but this arrangement seems to address most of your concerns.
If define_parameter_mapping returns None, then you can replace list with any, and then all of the function calls will get made, and you won't have to construct that dummy list.
I would go with Implementation 2, but it is a close call.
I think #2 and #3 are equally readable. Imagine if you had 100s of mappings... Either way, I cannot tell what the code at the bottom is doing without scrolling to the top. In #2 you are giving a name to the data; in #3, you are giving a name to the function. It's basically a wash.
Changing the data is also a wash, since either way you just add one line in the same pattern as what is already there.
The difference comes if you want to change what you are doing to the data. For example, say you decide to add a debug message for each mapping you define. With #2, you add a statement to the loop, and it is still easy to read. With #3, you have to create a lambda or something. Nothing wrong with lambdas -- I love Lisp as much as anybody -- but I think I would still find #2 easier to read and modify.
But it is a close call, and your taste might be different.
I think #3 is not bad although I might pick a slightly longer identifier than d, but often this type of thing becomes data driven, so then you would find yourself using a variation of #2 where you are looping over the result of a database query or something from a config file
There's no right answer, so you'll get opinions on all sides here, but I would by far prefer to see #2 in any code I was responsible for maintaining.
#1 is verbose, repetitive, and difficult to change (e.g. say you need to call two methods on each pair or add logging -- then you must change every line). But this is often how code evolves, and it is a fairly familiar and harmless pattern.
#3 suffers the same problem as #1, but is slightly more concise at the cost of requiring what is basically a macro and thus new and slightly unfamiliar terms.
#2 is simple and clear. It lays out your mappings in data form, and then iterates them using basic language constructs. To add new mappings, you only need add a line to the array. You might end up loading your mappings from an external file or URL down the line, and that would be an easy change. To change what is done with them, you only need change the body of your for loop (which itself could be made into a separate function if the need arose).
Your complaint of #2 of "object before verb" doesn't bother me at all. In scanning that function, I would basically first assume the verb does what it's supposed to do and focus on the object, which is now clear and immediately visible and maintainable. Only if there were problems would I look at the verb, and it would be immediately evident what it is doing.

python style: inline function that needs no inlining?

I'mm writing gtk code. I often have short callbacks that don't need to be closures, as they are passed all the parameters they need. For example, I have this in a loop when creating some gtk.TreeViewColumns:
def widthChanged(MAINCOL, SPEC, SUBCOL, expandable):
if expandable: return
w = MAINCOL.get_width()
SUBCOL.set_fixed_width(w)
cl.connect("notify::width", widthChanged, pnlcl, expand)
This is probably inefficient, since the function is being created on every iteration of the loop (side-question: is it actually, or is it optimized?). However, I feel like if I moved all these one-liners to the top level, the code would be more confusing. Any opinions?
Go with whatever style is most readable. Don't worry about speed unless your code profiling tools have told you that the area is a hotspot.

Iterate a certain number of times without storing the iteration number anywhere [duplicate]

This question already has answers here:
Is it possible to implement a Python for range loop without an iterator variable?
(15 answers)
Closed 5 years ago.
I was wondering if it is possible to perform a certain number of operations without storing the loop iteration number anywhere.
For instance, let's say I want to print two "hello" messages to the console. Right now I know I can do:
for i in range(2):
print "hello"
but then the i variable is going to take the values 0 and 1 (which I don't really need). Is there a way to achieve the same thing without storing those unwanted values anywhere?
Needless to say, using a variable is not a big deal at all... I'm just curious.
The idiom (shared by quite a few other languages) for an unused variable is a single underscore _. Code analysers typically won't complain about _ being unused, and programmers will instantly know it's a shortcut for i_dont_care_wtf_you_put_here. There is no way to iterate without having an item variable - as the Zen of Python puts it, "special cases aren't special enough to break the rules".
exec 'print "hello";' * 2
should work, but I'm kind of ashamed that I thought of it.
Update: Just thought of another one:
for _ in " "*10: print "hello"
Well I think the forloop you've provided in the question is about as good as it gets, but I want to point out that unused variables that have to be assigned can be assigned to the variable named _, a convention for "discarding" the value assigned. Though the _ reference will hold the value you gave it, code linters and other developers will understand you aren't using that reference. So here's an example:
for _ in range(2):
print('Hello')
Others have addressed the inability to completely avoid an iteration variable in a for loop, but there are options to reduce the work a tiny amount. range has to generate a whole bunch of numbers after all, which involves a tiny amount of work; if you want to avoid even that, you can use itertools.repeat to just get the same (ignored) value back over and over, which involves no creation/retrieval of different objects:
from itertools import repeat
for _ in repeat(None, 200): # Runs the loop 200 times
...
This will run faster in microbenchmarks than for _ in range(200):, but if the loop body does meaningful work, it's a drop in the bucket. And unlike multiplying some anonymous sequence for your loop iterable, repeat has only a trivial setup cost, with no memory overhead dependent on length.
Although I agree completely with delnan's answer, it's not impossible:
loop = range(NUM_ITERATIONS+1)
while loop.pop():
do_stuff()
Note, however, that this will not work for an arbitrary list: If the first value in the list (the last one popped) does not evaluate to False, you will get another iteration and an exception on the next pass: IndexError: pop from empty list. Also, your list (loop) will be empty after the loop.
Just for curiosity's sake. ;)
This will print 'hello' 3 times without storing i...
[print('hello') for i in range(3)]
Sorry, but in order to iterate over anything in any language, Python and English included, an index must be stored. Be it in a variable or not. Finding a way to obscure the fact that python is internally tracking the for loop won't change the fact that it is. I'd recommend just leaving it as is.
for word in ['hello'] * 2:
print word
It's not idiomatic Python, but neither is what you're trying to do.
You can simply do
print 2*'hello'

Categories