Welcome to Programming is Magic, lesson 2. In this lesson we will build on the spells that we cast in lesson one. We will learn about the different ingredients you can use in your spells, including how to cast spells that can change based on how the user of the spell interacts with it. We will also learn how to capture bits of magic for use later, or use variables, and we will also encounter operators. The arcane knowledge that you may already have is that computers store all information as 0s and 1s. Or to be precise, electric charges of 0 volts or 5 volts. Or to be even *more* precise, charges of about 2.5v or less or charges of more than 2.5v. This is some of the magic that allows programming, though this knowledge isn't important for our lesson. To recap our previous lesson, the most basic spellcasting is printing information: >>> print("Reviews are boring") Reviews are boring >>> print('But magic', 'is cool!') But magic is cool! We can use single or double quotes, and we can use triple-quoted strings if we want to span multiple lines. We can cast spells to print multiple strings. But if this was all the magic to be found in programming it would be... boring. And you probably wouldn't be reading this. In the mundane world, we simply write things and guess what they mean based on what they look like and what we've been taught. So if you saw something like: >>> print("2+3") You might expect to see `5`, or you might know that from school. However, the computer doesn't see that as the numbers 2, 3, and addition. It just sees the *string* "2+3". To the computer, it only means "here is some text". If we want to cast a spell that can do math, we need to remove the quotes: >>> print(2+3) 5 In our last lesson we learned that we can print multiple strings, so... can we mix strings and math in the same spell? Well, try it! >>> print("The sum of 2+3=", 2+3) The sum of 2+3= 5 (If you're frustrated with the output of this spell, you can write it a different way that we will discuss more in a future lesson) >>> print("The sum of 2+3=", 2+3, sep="") The sum of 2+3=5 These types of spell ingredients are called _integers_. Python, and many other magic systems (programming languages) will typically have: - strings (and some will have a type for individual characters, but Python just has strings of length 1) - integers - the whole numbers, e.g. 1,2,3,-42,99,85,1234567890 - floats - 4.2, 9.3, 3.333333333 - "floating point decimals" is their proper name. You will be tempted to think they work like regular decimals but they have some tricky things to be aware of, and have caused a number of problems for the wizards who were careless or ignorant. - bool or boolean - True and False. Some languages use use 1 and 0 for these values. - None - a specal ingredient where, like the Highlander, there can be only one (more on that later). There are some other more advanced datatypes that we'll cover in later lessons, but these will be a good start. Up to this point, we have been casting our spells with `print`, and if we have put our spells in a file we'll need to use that to get them out, but Python's interpreter is called a REPL, or "Read, Eval, Print, Loop". When we cast our spells, the Python magic reads what we put, evaluates it, prints the result, and loops again. >>> "Hello" 'Hello' >>> 2+3 5 >>> 9*7 63 >>> 20+22 42 >>> 9+1/2 9.5 The way our magic works: >>> is the prompt that is waiting for our input. When we input the ingredient "Hello", Python evaluates it and prints the result, which is the string 'Hello'. Then loops to a new prompt >>>. Then we cast 2+3, Python reads, evaluates, and prints 5. And so on and so forth. We can combine some of our ingredients: >>> "Programming " + "is" + " magic" 'Programming is magic' >>> 3+2.4 5.4 But if we try to combine others, our magic will backfire and we'll see an exception: >>> "2+3="+2+3 Traceback (most recent call last): File "", line 1, in TypeError: can only concatenate str (not "int") to str But *some* spells do work! >>> "Programming is " + "Magic"*10 'Programming is MagicMagicMagicMagicMagicMagicMagicMagicMagicMagic' You may have noticed that there is a difference between casting `print` and just writing a string: >>> print("Differences") Differences >>> "Differences" 'Differences' >>> True True >>> None >>> In our protection circle of the REPR, when we write something on the prompt, the value that is printed is the result. `print` has no result -- it displays the output and then None is the result. So the result of a string is a string, and the result of printing a string is that the string is printed and then there is None. Confusing, but True! >>> print("Differences is None?") is None Differences is None? True The operator `is` is a special kind of magic that confirms the identity of the values: >>> True is True True >>> False is False True >>> None is None True >>> 3 is 4 :1: SyntaxWarning: "is" with a literal. Did you mean "=="? False Well this is new! If you're using a sufficiently advanced magic system, or version of Python, you'll see that SyntaxWarning. Because `is` is a spell that checks for `identity`. Python helpfully suggests `==`, which might seem weird. Why two equals signs? What if we try a single equals? >>> 3 = 4 Original exception was: File "", line 1 SyntaxError: cannot assign to literal If you're familiar with math, you might have expected `=` to mean "equals", but in our magic system, `=` means _assignment_. Instead, `==` means equals: >>> 3 == 4 False >>> 3 == 4-1 True What is assignment then? Well, in Python assignment is a way that you can store values, by assigning them a name. >>> name = "Bob" >>> name 'Bob' >>> print(name) Bob >>> print("Hello,", name) Hello, Bob This is where we really start to cast some neat magic. We mentioned at the start of the lesson that we were going to allow the users to interact with our spells, and we're going to do that now with `input` >>> input("What is your name? ") What is your name? Gandalf 'Gandalf' But like we did with our name previous, we can assign a name to the result of input: >>> your_name = input("What is your name? ") What is your name? King Arthur >>> your_quest = input("What is your quest? ") What is your quest? We seek the grail >>> your_color = input("What is your favorite color? ") What is your favorite color? Blue >>> print("Hello,", your_name, ", I hope you have good luck while", your_quest, "and I'm glad to hear you like the color", your_color,"- I personally like yellow!") Hello, King Arthur , I hope you have good luck while We seek the grail and I'm glad to hear you like the color Blue - I personally like yellow! We can also compare some values: >>> the_number = 5 >>> guess = input("Guess a number: ") Guess a number: 5 >>> guess == the_number False Hrm. That's not right, is it? Something went wrong with our spell. Ah, yes! This is one of those logic errors that we talked about, and is specifically related to the _type_ of the values. Let's add the `type` spell to our spellbook. This allows us to discover what the type of a value is: >>> type(3) >>> type(3.0) >>> type(guess) >>> type(the_number) >>> the_number 5 >>> guess '5' Ah-hah! Here is an important lesson to remember: Magic, or Programming, will do _exactly_ what you asked it to -- no matter what your intent was. While some magics require intent, our magic systems find intent completely irrelevant and will *only* perform the spell that you cast. In our case, we asked Python if a str was equal to an int, which will never be! If we want them to be comparable we have to first `convert` them to the same type. And we do that by casting a spell on one or the other to turn them into the correct type. >>> guess = int(guess) >>> guess == the_number True We can easily cast from one type to another, but because our *intent* is irrelevant, with a slight twitch with your wand you may inadvertently cast an entirely different spell: >>> guess = input("guess: ") guess: 3 >>> geuss == '3' Traceback (most recent call last): File "", line 1, in NameError: name 'geuss' is not defined Do you see my error? I mis-timed the flick of my wand and typed `eu` instead of `ue`. Here, Python was gracious enough to give me a NameError, but beware! >>> geuss = int(guess) >>> guess == 3 False Egads! I did it again! Rather than assigning to `ue` I created a new variable and assigned it `int(guess)`. `guess` itself remains unchanged. Again: Programming doesn't care about your *intent* -- it will do *exactly* what you told it to. If you are casting spells and the output of the spells are not what you example, remember: **there MUST be a reason**. No matter how much you think your spell should work one way, if it's working another way there is always a reason for it. It may be as simple as an extra `s` on `guesss`. Or a `+` where you meant `-`. When you're struggling to find the problem, have another person take a look at your spell. Or explain it out loud to a rubber duck. Or re-write what you think you wanted and see if you get the same result. Sometimes you may need to get up and go for a walk before you can tame that dragon. But you *can* do it. After all, you're a wizard! So, we know how to cast spells to print and get input, and attach names to our values. Some might say that we're _storing_ values in the variable, and for some magic systems you do! But in Python, you're actually **naming** the value. >>> x = 3 >>> y = 3 >>> x == 3 True >>> x == y True >>> x is y True >>> x = "hi" >>> y = "hi" >>> x == y True >>> x is y True >>> x = "Programming is magic" >>> y = "Programming" + " is magic" >>> x == y True >>> x is y False Those were quite a few spells, and if you look closely... they didn't all appear to behave the same. `x is y` was true with "hi" but not with "Programming is magic". Is it simply because we did addition? >>> x = 'hi' >>> y = 'h'+'i' >>> x == y True >>> x is y True No? Well. That seems inconsistent! And if you're not aware of this behavior you can spend a lot of time getting very frustrated that your spells aren't coming out quite right. The simple rule to follow is that you use `==` when you want to know if things are **equal**, but `is` if you want to know if things are the same thing. For instance, Gandalf the Grey, Mithrandir, Incánus, and Tharkûn are all names that refer to the same Gandalf. You may have a name and a nickname, or a name and a full name. Such as The Ring Bearer, Frodo, and Frodo Baggins all refer to the same person. If we were to cast that spell, we would do it like this: >>> frodo = "Frodo Baggins" >>> the_ring_bearer = frodo >>> frodo_baggins = frodo In this type of magic we can combine that into one: >>> frodo = the_ring_bearer = frodo_baggins = "Frodo Baggins" >>> frodo is the_ring_bearer True >>> the_ring_bearer is frodo_baggins True >>> the_ring_bearer is frodo_baggins is frodo True This will become particularly important in future lessons where we begin talking about mutable types and collections, but for these simple spells it's not as essential. The simple rule of `==` for equality and `is` for identity is enough. We've cast spells for input, output, and to name things and understand their identity, as well as their types, and we've done some basic math, like addition, subtraction, multiplication, and division. In the final part of this lesson, we will cover the other spells for the comparison operators. >>> 3 > 4 False >>> 3 < 4 True >>> 3 <= 4 True >>> 3 >= 4 False >>> 3 == 4 False These are the comparisons that we can make, and can be handy for things like: >>> height = input("How tall are you in inches? ") How tall are you in inches? 72 >>> height = int(height) >>> print("Are you tall enough?", height > 36) True >>> height = int(input("Height: ")) Height: 36 >>> print("Are you tall enough?", height > 36) False Or: >>> secret = "open sesame" >>> guess = input("What is the password? ") What is the password? secret! >>> print("Access is granted?", guess == secret) Access is granted? False You may have noticed that we can connect our spells on one line, like we did with `int(input(...))`. When we don't care about the results in a certain form, it can be better to simply convert it as soon as we get it. In this lesson we covered: - input and output spells - identity and equality (`is` and `==`) - types (str, int, float, bool, and None) - operators (*, +, /, -) - variables and assignment In our next lesson, we will move outside of the REPL and start writing down our spells in files, and we will also learn about conditionals and looping! Before we move onto that kind of magic, make sure that you're comfortable with input and output, comparing strings, comparing numeric values, and using the mathematic operators. When you're ready, let's start building up spells that we can easily cast over and over again!