{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Tutorial: What are strings?\n", "\n", "Strings are just arrays of characters. They are immutable - they can't be edited \"in place\". However, there are a lot of methods for cutting strings apart and gluing them back together again. So a typical string edit consists of taking part(s) of an existing string, transforming them (eg, converting to lower case, taking out white space), then gluing the results back together using + or f\"string\". \n", "\n", "A character is a single letter or symbol. They are represented internally as numbers - Google \"ASCII table\" - that we interpret as letters/numbers/symbols/whitespace, etc. \n", "\n", "Strings are primarily used for displaying data in a \"human readable\" form, but they can also be used as parameters to functions and as keys in dictionaries. \n", "\n", "The primary \"gotcha\" of strings is that the string \"3.5\" is NOT the number 3.5 - it is three characters (\"3\" \".\" \"5\") that can be converted to an actual number (using casting, eg int(3)). Similarly, numbers can be converted to strings in order to print them out (eg f\"{x}\" will turn x into a string).\n", "\n", "There are many utilities/functions for converting strings to specific data types (like numbers, dates, times) and back again. \n", "\n", "There are also many methods (strip() or replace() being the most common) for removing leading/trailing white space (strip) or replacing troublesome characters (like ' and -)\n", "\n", "These will be covered in b_tutorial_strings_edit_search.ipynb\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Printing out variables or displaying them in the debugger\n", "This is probably the most common use for strings. You should (almost) always do some formatting/additional text to explain what you're printing out. These first examples just demonstrate using strings in print functions without much formatting.\n", "TODO Edits to make/try\n", "- change the order of prints\n", "- add in some additional text (eg - print \"foo\" before printing a_string)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# A random collection of variables of different types\n", "a_floating_point_number = 3.6720\n", "an_integer = 6\n", "a_string = \"I am a string\"\n", "a_string_with_symbols = \"% {} begin\\n 'end\" # Yes, you can put ' in a \"\", or \" in a ''" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Make a list out of all of those items\n", "a_list_of_all = [a_floating_point_number, an_integer, a_string, a_string_with_symbols]" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Print out the variable, no formatting\n", "# You can print out more than one thing by separating them with a comma\n", "print(a_floating_point_number)\n", "print(a_floating_point_number, an_integer, a_string)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# What python is doing underneath when you call print:\n", "# Convert the variable to a string\n", "a_temp_str = str(a_floating_point_number) # a_temp_str is a string\n", "\n", "# print JUST takes strings - but it automatically converts the things in parentheses to strings before printing\n", "print(a_temp_str) # Just prints the string\n", "print(a_floating_point_number) # Converts a_floating_point_number to a string, then prints that string" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# What does this look like? Why does it not look exactly like the a_string_with_symbols?\n", "print(a_string_with_symbols)\n", "\n", "# answer: The \\n is a \"special\" character that puts in a new-line - print finds any of these and replaces them\n", "# with a new line when it writes to the console" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Why does this work? What is it doing?\n", "print(a_list_of_all)\n", "# answer: a list is always printed as [ , , ] with print called for each element. Note that, in this case, because\n", "# a_string_with_symbols is already a string, it just prints it out - and does NOT interpret the \\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# An aside: If you want to print out each of the elements in turn in the string, you can do it this way:\n", "for item in a_list_of_all:\n", " print(item)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Tutorial: Formatting a string\n", "\n", "This is how you create a \"nice\" string for printing - like adding the variable's name and units. You should always make \"nicely\" formatted strings for printing. \n", "\n", "Yes, this syntax is weird. Think of it as calling the string function\n", " **format(\"string to format var 1 (put first variable here) var 2 (put second variable here)\", first variable, second)**\n", "\n", "The curly brackets tell the format function to \"fill in\" the variables that are passed in. Python provides a much nicer way of doing this with **f(\"\")** which we'll show below. It's useful to see the more traditional, C++ or Matlab version first.\n", "TODO edits to try/do\n", " - add in a 3rd variable to pring\n", " - print with 5 decimal places\n", " - print out the same variable twice in one line" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Note that I've split up the creating of the string from the printing here....\n", "a_formatted_str = \"String to format var 1 {} var 2 {}\".format(a_floating_point_number, an_integer)\n", "print(a_formatted_str)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# but you can do both in one line\n", "print(\"String to format var 1 {} var 2 {}\".format(a_floating_point_number, an_integer))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### {} format\n", "The {} syntax can do A LOT - left indent, print with a certain precision, etc. In this example, the first parameter is printed twice, once with no formatting, once with fancy formatting, with a special character (the /n) to separate the lines.\n", "\n", "See here: https://docs.python.org/3/library/string.html#formatspec for a full list of functionality.\n", "\n", "Note that you can use \"\" for specifying a string OR ''. This is really handy, because if, for example, you want a string with a ' in it, just use \"\" to create the string." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# If no numbers are given in the {} it takes them in order; there need to be as many variables as {}\n", "print('No formatting: {}\\nFormatted: {:.2}'.format(a_floating_point_number, a_floating_point_number))\n", "# If numbers are given then it takes them in that order (indexing from 0)\n", "print(\"No formatting: {0}\\nFormatted: {0:.2}\".format(a_floating_point_number))\n", "# Or you can use names - in this case, x, to refer to the parameter\n", "print('No formatting: {x}\\nFormatted: {x:.2}'.format(x=a_floating_point_number))" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Note that you can use the '+' with strings to \"glue\" a bunch of strings together if you don't need the full functionality\n", "# of the format function - this can be handy, for example, for creating a file name:\n", "data_dir = \"home/directory/directory/\"\n", "file_name = \"my_file_name\"\n", "file_type = \"csv\"\n", "str_file_name = data_dir + file_name + \".\" + file_type\n", "print(str_file_name)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## More convenient: The f\"\" syntax\n", "If you don't like typing, Python provides a \"shortcut\" for \"\".format() called f-strings\n", "\n", "See, eg, https://realpython.com/python-f-strings/\n", "\n", "Basically, this takes out the explicity .format part and lets you put variables directly in the {}. \n", "\n", "\n", "This is really useful if you're just trying to print out values of variables.\n", "\n", "TODO edits to try/do\n", "- add in another variable to print\n", "- format with exponential" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "f_str = f\"No formatting: {a_floating_point_number}\\nFormatted: {a_floating_point_number:.2}\"\n", "print(f_str)\n", "\n", "# and in one line\n", "print(f\"No formatting: {a_floating_point_number}\\nFormatted: {a_floating_point_number:.2}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# GOTCHAS with strings\n", "The following are some common errors/things that work/things that don't work. These first examples convert strings to numbers using float() or int(). \n", "\n", "An aside: float() and int() are examples of type casting - convert to the given type. Python often does this for you - for example, print(3.5) actually does print(str(3.5))." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Converting strings to numbers - this mostly just \"works\".\n", "a_number = float(\"3.57\")\n", "an_exp_number = float(\"3.3e-3\")\n", "an_integer = int(\"3\")\n", "print(f\"Number: {a_number}, exp number: {an_exp_number} integer {an_integer}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## GOTCHA - converting a string to a number when the string is not a number\n", "Python will throw an error if, for example, you try to convert a string that isn't a number - try running this:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "a_string_that_was_suppose_to_be_a_number = \"a3.2\"\n", "bad_number = float(a_string_that_was_suppose_to_be_a_number)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "... The reason this is a ValueError is because, with the *a* in the string, this can't be converted to a number. \n", "\n", "You'll usually get a ValueError when you *think* your variable is a string representing a number, but it isn't (usually because you are splitting up a longer string and the split went wrong)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## GOTCHA - weird behavior with a number and a string\n", "Why you need to know that strings are arrays of numbers, NOT the number:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "my_str_not_a_number = \"3.6\"\n", "# Unexpected string behavior - the '*' is interpreted as make 3 copies of the string\n", "my_str_times_3 = my_str_not_a_number * 3\n", "print(my_str_times_3)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Why did you get that? Well, Python knows that your string is a string, and the * operator is defined as make n copies of that string... Which is maybe weird, but it can be handy sometimes (see *join* example below).\n", "\n", "Of course, if we try multiplying the string **my_str_not_a_number** by 3.5 (which is a floating point number) we'll get an error, because Python doesn't have a defined behavior for multiplying a string by a floating point number.\n", "\n", "Aside: Wait, it says *can't multiply sequence*, not multiply string, why? Well, recall that a string is just a sequence of characters... " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "my_str_times_3 = my_str_not_a_number * 3.5" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## GOTCHA - Can't change an element of a string\n", "Strings are immutable. Which means you can't change the string \"in place\"" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "my_str = \"A long string\"\n", "# Attempting to convert the first character to a lower case\n", "my_str[0] = 'a'" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "When it says \"does not support item assignment\" the translation is:\n", "Strings are arrays (lists)\n", "You tried to take an item (the first character, my_str[0]) and change it by assigning a new value (= 'a')\n", "\n", "We'll cover more on editing strings in the next tutorial, but to edit a string you actually make a new string from the old ones, which we'll do now:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# The right way to do this is to create a new string with the change and assign it to the same variable\n", "my_str = my_str.lower()\n", "print(my_str)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# OR\n", "my_str = \"A long string\"\n", "my_str = 'a' + my_str[1:]\n", "print(my_str)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# BEGIN Optional: Using join to print a list nicely\n", "You can use the **.join** function on a list to format a string from a list in one command. This can be very useful when you want to print out a list in as little space as possible.\n", "\n", "An aside: The syntax of **join()** looks a lot like the syntax **\"{}\".format()** we saw at the top of the tutorial. They're both functions/methods on strings. The syntax is, again, a bit weird. See taking it apart below." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# This doesn't do quite what you want\n", "a_list_of_strings = [\"Hi\", \"how\", \"are\", \"you?\"]\n", "print(str(a_list_of_strings))" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Instead use join, which is a bit closer - it at least makes one string\n", "print(\"\".join(a_list_of_strings))" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# ... and once again, with spaces...\n", "print(\" \".join(a_list_of_strings))" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# ... and one more time with numbers...\n", "list_of_numbers = [0.3 + 0.1 * n for n in range(0, 10)]\n", "print(\", \".join([\"{:.2}\"] * len(list_of_numbers)).format(*list_of_numbers))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Taking it apart** Especially when you're copying something complicated from a website, it can be really handy to \"take it apart\". Here we take apart the join:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Uses the * with a string to make multiple copies, one for each element in the list\n", "list_of_n_curlies = [\"{:.2}\"] * len(list_of_numbers) \n", "print(list_of_n_curlies)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Use join to convert the list to one string\n", "curlies_with_commas = \", \".join(list_of_n_curlies) \n", "print(curlies_with_commas)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# The *list_of_numbers is puts each item in in turn, and is the same as l[0], l[1], l[2]... l[n]\n", "#. No, this is NOT the same as list_of_numbers[:] - it \"feeds\" each element in one at a time\n", "fill_in_curlies = curlies_with_commas.format(*list_of_numbers)\n", "print(fill_in_curlies)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# In more traditional code, this would be written as\n", "my_str_list = \"\"\n", "for n in list_of_numbers:\n", " str_n = \"{:.2}\".format(n) # Convert n to a string, with formatting\n", " my_str_list = my_str_list + str_n + \", \" # append the string for n with a ,\n", "my_str_list = my_str_list[0:-2] # Take out the last ,\n", "print(my_str_list)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## END Optional Join" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.11.4" } }, "nbformat": 4, "nbformat_minor": 2 }