<jupyter_start><jupyter_text>Tutorial 1: Python Basics*ASTR 211: Observational Astronomy, Spring 2021* \*Written by <NAME>* Welcome to this introduction to programming with Python. If you have little to no experience with coding, that's totally fine! This is the place for you to start. If you already know some Python, I ask you to skim this document but, if there's nothing here you don't already know, you're welcome to move on. This notebook is going to cover all the basic principles of programming with Python 3, your average astronomer's language of choice. It's a high-level scripting language, meaning it's pretty easy to read and is mostly used for doing math or other automated tasks. Another thing to note about Python is that it doesn't need to be compiled before its run like some other languages, meaning that when you run a Python program, the computer reads it top to bottom, line by line, until it hits the end of the program or an error.It's one of, if not *the* best language for beginners, so don't worry if this document looks daunting! *** CommentsFirst off, the Markdown cells in Jupyter are nice, but you may want a way to take notes on your code when you're not using Jupyter. That's where *comments* come in. They're pieces of the code demarcated by certain symbols(hashtags and triple quotes) that tell Python not to read them as code. For example, a single-line comment looks like this:<jupyter_code># Nothing will happen if this code is run!<jupyter_output><empty_output><jupyter_text>If you want to write multiple lines of comments, however, you can either create multiple single-line comments, or a multi-line comment, like this:<jupyter_code># This is a series
# of 3 single line comments
# which get a bit tiring...

'''
Here's a multiple line
comment, contained within the tick
marks, which lets me go as long as I want!
(Python won't read this as code, either!)
'''<jupyter_output><empty_output><jupyter_text>Remember to *always comment your code* so you (and your instructors) can always see what its supposed to be doing! *** Basic arithmeticPython is great at math. For example, it can add, subtract, multiply and divide:<jupyter_code>2 + 2
3 * 3
4 - 4
5 / 5<jupyter_output><empty_output><jupyter_text>It can exponentiate:<jupyter_code>3**2<jupyter_output><empty_output><jupyter_text>And do scientific notation:<jupyter_code>5E6<jupyter_output><empty_output><jupyter_text>It can also perform modular arithmetic with the symbol `%`. This kind of calculation is like dividing two numbers and only keeping the remainder:<jupyter_code>10 % 5 # Zero, because 5 goes into 10 exactly twice
10 % 6 # Four, because 6 goes into 10 only once, leaving 4 remaining<jupyter_output><empty_output><jupyter_text>It can do lots of these things at the same time, following PEMDAS:<jupyter_code>1 + (6 * 9.1) - (7 / 9)<jupyter_output><empty_output><jupyter_text>Python reads out a lot of decimal places by default. If you only want a few, you can use the `round` function:<jupyter_code>round(1 + (6 * 9.1) - (7 / 9), 2) # Rounds to 2 decimal places<jupyter_output><empty_output><jupyter_text>If you want to use special functions (trigonometry, logarithms, etc), you'll have to import a *library* that contains those functions. Libraries are just that -- collections of functions that you can Python's built-in library for this purpose is aptly named `math`. To import the library, simply add the line `import math` to your code.<jupyter_code>import math<jupyter_output><empty_output><jupyter_text>It's best practice to import your libraries at the beginning of your file. Again, Python reads and runs code line-by-line until it gets an error or finishes running the program. In the same vein, if you use a function in the `math` library before your code knows you've imported it, Python won't know what you're talking about when you reference it. In order to use a function from the `math` library (or any library), we use what's called _dot notation_, wherein you call the function you want as `.`. For example, if I wanted to use the log-base-10 function in the `math` library, I would write<jupyter_code>math.log10(5)<jupyter_output><empty_output><jupyter_text>The `math` library has other useful functions as well:<jupyter_code>print(math.cos(90), math.log(2.71))<jupyter_output>-0.4480736161291701 0.9969486348916096<jupyter_text>*** PrintingSay you want to see the result of one of the calculations above. Jupyter is cool in that it'll just run the expressions above, no sweat. However, there are situations where you may want to tell Python explicitly to show you the answer (i.e. in code written with a text editor and executed in the terminal). For that, we use a *print statement* to print to the terminal:<jupyter_code>print(1 + (2 * 3) / (4 - 5))<jupyter_output>-5.0<jupyter_text>Notice that the expression is inside parentheses. If you're using an older version of Python, simply typing `print` before the expression would suffice. In Python 3.x, however, you need these parentheses!If you want to print multiple things at a time, that works too! Just separate them with a comma:<jupyter_code>print('Howdy!', 1232, 5.6)<jupyter_output>Howdy! 1232 5.6<jupyter_text>*** Data typesThere a few main data types to start with. You've seen the first two in action: Integer (int)Integers are just whole numbers -- 1, 2 10101, 69, etc. FloatA.K.A. "floating-point numbers," floats are numbers with decimals -- 1.6, 4.20, 80.0000085, etc. String (str)Strings are, well, strings of characters, which can include numbers and symbols. They're distinguished from other pieces of code by being surrounded by single or double quotes -- "Hi!", "ab6f8jb-.-.", etc.We've seen strings in the print statements above. Strings and the other data types cannot be used together in artithmetic, but there are some exceptions. For example, strings can be multiplied by integers to repeat themselves:<jupyter_code>print('abcde' * 5) # This should print 'abcde' 5 times<jupyter_output>abcdeabcdeabcdeabcdeabcde<jupyter_text>Strings can also be added together in a process called *string concatenation*:<jupyter_code>print('Hello' + ' ' + 'world!')<jupyter_output>Hello world!<jupyter_text>There are some useful characters you can add to your strings that will actually alter the way they look. Because there isn't really a "new line" button, and adding extra spaces to your strings for tabs is annoying, you can use `\t` and `\n` in those cases, respectively:<jupyter_code>print('a\nb\nc') # Should print a, b, and c on different lines
print('a\tb\tc') #Should print a, b and c with tab space in between them<jupyter_output>a	b	c<jupyter_text>(There's plenty more we can do with strings, which we'll get to in the next tutorial.)  ListsAnother very important data type is the list, which allows you to store multiple values in one variable. Lists in Python can contain values of all the same data type or different data types.<jupyter_code>print([1,2,3,4,5]) # All integers
print([1,'a','b',2]) # Some integers, some strings<jupyter_output><empty_output><jupyter_text>And, yes, you can have lists of lists:<jupyter_code>print([[1,2,3], [5,6,7], [1,4,5]])<jupyter_output><empty_output><jupyter_text>Just like strings, you cannot subtract or divide lists from one another, but you can add them together and multiply them by integers:<jupyter_code>print([1,2,3,4] + [1,2,3,4])
print([1,2,3,4]*3)<jupyter_output><empty_output><jupyter_text>We'll talk about both strings and lists in more depth in the next tutorial, including how to access the values inside them. For now, though, it's enough to know that they exist.  Boolean (bool)Booleans can either be True or False, 1 or 0. *Note that boolean truth values are always capitalized!*You can probably come up with examples of boolean statements if you think about it. For example, the fact that "two is equal to two" is True, and "three is equal to two" is False. In Python, the equals sign is reserved for assigning variables, however, so boolean statements like this are written with a double-equals sign, `==`:<jupyter_code>print(3 == 3) # Should be true
print(3 == 4) # Should be false<jupyter_output>False<jupyter_text>The `==` symbol is called a *comparison operator*, and the others include "not equal to" (`!=`), "less than" (``), and "greater than or equal to" (`>=`):<jupyter_code>print(6 > 7) # False
print(6 < 7) # True 
print(7 >= 7) # True
print(7 != 7) # False<jupyter_output>False<jupyter_text>These sorts of comparisons can be done between numbers, strings, and objects of different data types:<jupyter_code>print('dog' == 'cat') # False
print('42' == 42) # False<jupyter_output>False<jupyter_text>Notice the cell directly above this one. Despite the string version of 42 technically being the same number as 42, they aren't equal (but the comparison didn't raise an error). If you're unsure of what kind of data type something is, you can check it with the `type()` function, like so:<jupyter_code>print(type(12)) # int<jupyter_output><class 'int'><jupyter_text>If you wanted to make that previous statement true, however, you could convert one of the two values to be the same type as the other (which comes in handy really often). This is called *typecasting*:<jupyter_code>print('42' == str(42)) # True, because I've converted the integer 42 into the string '42'
print(int('42') == 42) # True, because I've converted the string '42' into the integer 42<jupyter_output>True<jupyter_text>You can also use this in boolean expressions:<jupyter_code>print(42 == 42.0) # True; both the same value, but one has a decimal
print(type(42) == type(42.0)) # False; same value, but one's an int and the other is a float!<jupyter_output>False<jupyter_text>Boolean expressions don't have to be "this" versus "that," they can contain any number of truth statements, connected by `and` & `or` operators:<jupyter_code>print((6 == 4) and (1 == 1)) # False, because one is true but not both
print((2 == 3) or (4 == 5)) # False, because neither is true<jupyter_output>False<jupyter_text>*** VariablesSo we know how to do math and stuff. But what if we want to hang onto the value of one of these expressions, and use it in a calculation later on? For this, we assign the value to a *variable*.<jupyter_code>x = 85<jupyter_output><empty_output><jupyter_text>Now that `x` has been assigned the value 85, we can reference `x` in the future to get the value back:<jupyter_code>print(x)<jupyter_output>85<jupyter_text>Variables can have any name you want, really. It's best practice to have them be all lowercase, and they cannot start with a number, however. Also, they can not share the name of a *function* (which we'll get into more later), a boolean value (True or False), or any Python keywords like `and`/`or` or `print` (the ones we've seen so far). They also can't have special characters like !, ?, etc., but they can contain underscores.Now, suppose I'm done with the variable `x` from above, and I want to reuse the name "x" for a different calculation. That's fine, because Python allows us to *reassign* variables later in the program. When Python runs, it reads code top-to-bottom, so whatever the most recent value of `x` was, that's what it'll go with on the next line. For example,<jupyter_code>x = 1
print(x) # This should print out 1

x = 4
print(x) # This should print out 4<jupyter_output>1
4<jupyter_text>Note that, if you accidentally reassign a variable, you can't get the old value back!You can also save the result of any kind of expression to a variable:<jupyter_code>y = 1 + 2
print(y)
z = (12 + 6) == 18 and 16 == 1
print(z)
w = 'Van Vleck Observatory' + ' at Wesleyan University'
print(w)<jupyter_output>Van Vleck Observatory at Wesleyan University<jupyter_text>And, of course, you can use variables in expressions (otherwise, how useful would they really be):<jupyter_code>a = 6
b = 7
c = 9

d = (a + b) * c

print(d)<jupyter_output>117<jupyter_text>If you're really desperate to save space in your code, you can also assign multiple variables in one line:<jupyter_code>x, y, z = 1, 2.0, "hi"

print(x,y,z)<jupyter_output>1 2.0 hi<jupyter_text>*** User inputSo you can assign values to variables in your code, but what if you want to be able to change those variables freely without editing it? For this, we turn to the *input function*, which will allow you to enter the value from a prompt for a specific variable.The `input()` function, like the `print()` function, takes in a string and prints it to the terminal. The difference is that, in the case of `input()`, the user will be able to enter a value directly into the terminal after this string appears. The string should be a prompt.For example, say I wanted to ask the user their age, and store it in a variable `age` for use later in the program. To do so, I would run the following code:<jupyter_code>age = input("Enter your age (in years): ")
# (In Jupyter, a text box appears, but in your terminal will be a blinking marker.)
print(age)<jupyter_output>21<jupyter_text>I entered an integer as my age, but is that the type of our variable `age`?<jupyter_code>type(age)<jupyter_output><empty_output><jupyter_text>As you can see, when we use `input()`, Python takes whatever we type in as a string. If, for example, we wanted to do some calculation with this age, the fact that it's a string would cause problems. We can get around this by typecasting the result:<jupyter_code>age = input("Enter your age (in years): ")
age = int(age)
print(type(age))<jupyter_output><class 'int'><jupyter_text>*** DebuggingMost of the time spent programming (and usually even more in the beginning) is dedicated to debugging your code. If your code doesn't work, you search for the bug that's breaking it. There are a few types of errors that Python may throw at you: Syntax errorsSyntax errors come up for things like typos and bad variable names:<jupyter_code>var! = 12 # Variable name has illegal characters
print(12 > 11 asd 1 == 1) # Misspelled 'and'<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.0856122813280971, 'fraction_numerical': 0.01935023205997858, 'mean_word_length': 5.258266309204647, 'pattern_counts': {'":': 0, '<': 129, '<?xml version=': 0, '>': 132, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '36', 'dir': 'jupyter-structured-clean-dedup', 'id': '293191', 'license': 'MIT', 'n_tokens_mistral': 4362, 'n_tokens_neox': 4073, 'n_words': 1988, 'path': 'files/astr211_tut1-1.ipynb', 'repo_name': 'mvtea/mvtea.github.io', 'size': '14011'}