Hello

Let's use our familiar "Hello, World!" to get started:

$ cat -n hello.py
     1    #!/usr/bin/env python3
     2
     3    print('Hello, World!')

The first thing to notice is a change to the "shebang" line. I'm going to use env to find python3 so I won't have a hard-coded path that my user will have to change. In bash, we could use either echo or printf to print to the terminal (or a file). In Python, we have print() noting that we must use parentheses now to invoke functions. (One difference between versions 2 and 3 of Python was that the parens to print were not necessary in version 2).

Variables

Let's use the REPL to play:

$ python3
Python 3.6.1 |Anaconda custom (x86_64)| (default, May 11 2017, 13:04:09)
[GCC 4.2.1 Compatible Apple LLVM 6.0 (clang-600.0.57)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> name = 'Gorgeous'
>>> print('Hello, ' + name)
Hello, Gorgeous

Here I'm showing that it's easy to create a variable called name which we assign the value "Gorgeous." Just as in bash, we can use it in a print statement, but we can't directly stick it into the string:

>>> print('Hello, name')
Hello, name

We have to use the + operator to concatenate it to the literal string "Hello, ":

>>> print('Hello, ' + name)
Hello, Gorgeous

We can also pass a list of arguments to print, but notice the extra space:

>>> print('Hello, ', name)
Hello,  Gorgeous

So, we've just found that Python will automatically put a space between all the arguments:

>>> print('foo', 'bar', 'baz')
foo bar baz

Arguments

To say hello to an argument passed from the command line, we need to a module which is just a package of code we can use:

$ cat -n hello_arg.py
     1    #!/usr/bin/env python3
     2
     3    import sys
     4
     5    args = sys.argv
     6    print('Hello, ' + args[1] + '!')

From the sys module, we call the argv function to get the "argument vector." This is a list, and, like bash, the name of the script is in the zeroth position (args[0]), so the first "argument" to the script is in args[1]. It works as you would expect:

$ ./hello_arg.py Sally
Hello, Sally!

But there is a problem if we fail to pass any arguments:

$ ./hello_arg.py
Traceback (most recent call last):
  File "./hello_arg.py", line 6, in <module>
    print('Hello, ' + args[1] + '!')
IndexError: list index out of range

We tried to access something in args that doesn't exist, and so the entire program came to a halt ("crashed"). As in bash, we need to check how many arguments we have:

$ cat -n hello_arg2.py
     1    #!/usr/bin/env python3
     2
     3    import sys
     4
     5    args = sys.argv
     6
     7    if len(args) < 2:
     8        print('Usage:', args[0], 'NAME')
     9        sys.exit(1)
    10
    11    print('Hello, ' + args[1] + '!')

If there are fewer than 2 arguments (remembering that the script name is in the "first" position), then we print a usage statement and use sys.exit to send the operating system a non-zero exit status, just like in bash. It works much better now:

$ ./hello_arg2.py
Usage: ./hello_arg2.py NAME
$ ./hello_arg2.py Sally
Hello, Sally!

On line 7 above, you see we can use the len function to ask how long the args list is. You can play with the Python REPL to understand len. Both strings (like "foobar") and lists (like the arguments to our script) have a "length." Type help(list) in the REPL to read the docs on lists.

>>> len('foobar')
6
>>> len(['foobar'])
1
>>> len(['foo', 'bar'])
2

Here is the same functionality but using two new functions, printf (from the base package) and os.path.basename:

$ cat -n hello_arg3.py
     1    #!/usr/bin/env python3
     2    """hello with args"""
     3
     4    import sys
     5    import os
     6
     7    args = sys.argv
     8
     9    if len(args) != 2:
    10        script = os.path.basename(args[0])
    11        print('Usage: {} NAME'.format(script))
    12        sys.exit(1)
    13
    14    name = args[1]
    15    print('Hello, {}!'.format(name))
$ ./hello_arg3.py
Usage: hello_arg3.py NAME
$ ./hello_arg3.py Sally
Hello, Sally!

Notice the usage doesn't have a "./" on the script name because we used basename to clean it up.

main()

Lastly, let me introduce the main function. Many languages (e.g., Python, Perl, Rust, Haskell) have the idea of a "main" module/function where all the processing starts. If you define a "main" function, most people reading your code would understand that the program ought to begin there. I usually put my "main" as the first def (the keyword to "define" a function), and then use a little test at the end of the program to see if the magical "double-under" name is equal to the string double-under "main." It's a bit of a hack, but it seems to be standard Python.

$ cat -n hello_arg4.py
     1    #!/usr/bin/env python3
     2    """hello with args/main"""
     3
     4    import sys
     5    import os
     6
     7    def main():
     8        """main"""
     9        args = sys.argv
    10
    11        if len(args) != 2:
    12            script = os.path.basename(args[0])
    13            print('Usage: {} NAME'.format(script))
    14            sys.exit(1)
    15
    16        name = args[1]
    17        print('Hello, {}!'.format(name))
    18
    19    if __name__ == '__main__':
    20        main()

Function Order

Note that you cannot put lines 19-20 first because you cannot call a function that hasn't been defined (lexically) in the program yet. To add insult to injury, this is a run-time error -- meaning the mistake isn't caught by the compiler when the program is parsed into byte-code; instead the program just crashes.

$ cat -n func-def-order.py
     1    #!/usr/bin/env python3
     2
     3    print('Starting the program')
     4    foo()
     5    print('Ending the program')
     6
     7    def foo():
     8        print('This is foo')
$ ./func-def-order.py
Starting the program
Traceback (most recent call last):
  File "./func-def-order.py", line 4, in <module>
    foo()
NameError: name 'foo' is not defined

To contrast:

$ cat -n func-def-order2.py
     1    #!/usr/bin/env python3
     2
     3    def foo():
     4        print('This is foo')
     5
     6    print('Starting the program')
     7    foo()
     8    print('Ending the program')
$ ./func-def-order2.py
Starting the program
This is foo
Ending the program

Handle All The Args!

If we like, we can say hi to any number of names:

$ cat -n hello_arg5.py
     1    #!/usr/bin/env python3
     2    """hello with to many"""
     3
     4    import sys
     5    import os
     6
     7    def main():
     8        """main"""
     9        args = sys.argv
    10
    11        if len(args) < 2:
    12            script = os.path.basename(args[0])
    13            print('Usage: {} NAME [NAME2 ...]'.format(script))
    14            sys.exit(1)
    15
    16        print('Hello, {}!'.format(', '.join(args[1:])))
    17
    18    if __name__ == '__main__':
    19        main()
$ ./hello_arg5.py foo
Hello, foo!
$ ./hello_arg5.py foo bar baz
Hello, foo, bar, baz!

Look at line 16 to see how we can join all the arguments on a comma-space, e.g.,:

>>> ', '.join(['foo', 'bar', 'baz'])
'foo, bar, baz'
>>> ':'.join("hello")
'h:e:l:l:o'

Notice the second example where we can treat a string like a list of characters.

The other interesting bit on line 16 is how to take a slice of a list. We want all the elements of args starting at position 1, so args[1:]. You can indicate a start and/or end position. It's best to play with it to understand:

>>> x = ['foo', 'bar', 'baz']
>>> x[1]
'bar'
>>> x[1:]
['bar', 'baz']
>>> a = "abcdefghijklmnopqrstuvwxyz"
>>> a[2:4]
'cd'
>>> a[:3]
'abc'
>>> a[3:]
'defghijklmnopqrstuvwxyz'
>>> a[-1]
'z'
>>> a[-3]
'x'
>>> a[-3:]
'xyz'
>>> a[-3:26]
'xyz'
>>> a[-3:27]
'xyz'

Conditionals

Above we saw a simple if condition, but what if you want to test for more then one condition? Here is a program that shows you how to take input directly from the user:

$ cat -n if-else.py
     1    #!/usr/bin/env python3
     2    """conditions"""
     3
     4    name = input('What is your name? ')
     5    age = int(input('Hi, ' + name + '. What is your age? '))
     6
     7    if age < 0:
     8        print("That isn't possible.")
     9    elif age < 18:
    10        print('You are a minor.')
    11    else:
    12        print('You are an adult.')
$ ./if-else.py
What is your name? Geoffrey
Hi, Geoffrey. What is your age? 47
You are an adult.

On line 4, we can put the first answer into the name variable; however, on line 5, I convert the answer to an integer with int because I will need to compare it numerically, cf:

>>> 4 < 5
True
>>> '4' < 5
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unorderable types: str() < int()
>>> int('4') < 5
True

Types

Which leads into the notion that Python, unlike bash, has types -- variables can hold string, integers, floating-point numbers, lists, dictionaries, and more:

>>> type('foo')
<class 'str'>
>>> type(4)
<class 'int'>
>>> type(3.14)
<class 'float'>
>>> type(['foo', 'bar'])
<class 'list'>
>>> type(range(1,3))
<class 'range'>
>>> type({'name': 'Geoffrey', 'age': 47})
<class 'dict'>

As noted earlier, you can use help on any of the class names to find out more of what you can do with them.

So let's return to the + operator earlier and check out how it works with different types:

>>> 1 + 2
3
>>> 'foo' + 'bar'
'foobar'
>>> '1' + 2
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: must be str, not int

Python will crash if you try to "add" two different types together, but the type of the argument depends on the run-time conditions:

>>> x = 4
>>> y = 5
>>> x + y
9
>>> z = '1'
>>> x + z
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'int' and 'str'

To avoid such errors, you can coerce your data:

>>> int(x) + int(z)
5

Or check the types at run-time:

>>> for pair in [(1, 2), (3, '4')]:
...     n1, n2 = pair[0], pair[1]
...     if type(n1) == int and type(n2) == int:
...         print('{} + {} = {}'.format(n1, n2, n1 + n2))
...     else:
...         print('Cannot add {} ({}) and {} ({})'.format(n1, type(n1), n2, type(n2)))
...
1 + 2 = 3
Cannot add 3 (<class 'int'>) and 4 (<class 'str'>)

Loops

As in bash, we can use for and while loops in Python. Here's another way to greet all the people:

$ cat -n hello_arg6.py
     1    #!/usr/bin/env python3
     2    """hello with to many"""
     3
     4    import sys
     5    import os
     6
     7    def main():
     8        """main"""
     9        args = sys.argv
    10
    11        if len(args) < 2:
    12            script = os.path.basename(args[0])
    13            print('Usage: {} NAME [NAME2 ...]'.format(script))
    14            sys.exit(1)
    15
    16        for name in args[1:]:
    17            print('Hello, ' + name + '!')
    18
    19    if __name__ == '__main__':
    20        main()
$ ./hello_arg6.py Jack Jill
Hello, Jack!
Hello, Jill!

You can see more in the REPL:

>>> for letter in "abc":
...     print(letter)
...
a
b
c
>>> for number in range(0, 5):
...     print(number)
...
0
1
2
3
4
>>> for word in ['foo', 'bar']:
...     print(word)
...
foo
bar
>>> for word in 'We hold these truths'.split():
...     print(word)
...
We
hold
these
truths
>>> for line in open('input1.txt'):
...     print(line, end='')
...
this is
some text
from a file.

In each case, we're iterating over the members of a list as produced from a string, a range, an actual list, a list produced by a function, and an open file, respectively. (That last example either needs to suppress the newline from print or do rstrip() on the line to remove it as the text coming from the file has a newline.)

results matching ""

    No results matching ""