{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Lesson A4 – Objects" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This section should give a little more insight into what Python *objects* are and how you can handle them. We used the term object already, but it may still appear somewhat cryptic. We will only touch on the very basics and use simplifications here and there. The whole topic itself is pretty advanced. If you want to dive deeper into the fundamentals you may want to start [here](https://docs.python.org/3/tutorial/classes.html)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "## Variables" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We have used *variables* already in the previous lessons. We can assign a value to a variable in Python using the equal sign (`=`)." ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "# Assign to a variable\n", "number = 13.5" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "\n", "**Note:** Values are assigned to variables always from right to left using\n", " the equal sign (`=`).\n", "\n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now, `number` is a variable holding the floating point value `13.5`. When we assign a value to a variable, we *define* the variable. The value is now stored in the RAM (random access memory) of your computer at a specific location or *address*. We can think of the variable name as a label pointing to the address in memory where the value is stored. We can access the stored value via the variable label. We can also print out the address of the value in memory (its `id`), although this is hardly ever used in practice." ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "13.5\n", "The value 13.5 is stored at 139712208698320 (address in memory)\n" ] } ], "source": [ "print(number)\n", "print(f\"The value {number} is stored at {id(number)} (address in memory)\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Every variable we want to use must be defined. Doing anything with a variable that has not been defined yet, causes a NameError." ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "ename": "NameError", "evalue": "name 'unknown' is not defined", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)", "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0munknown\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mNameError\u001b[0m: name 'unknown' is not defined" ] } ], "source": [ "print(unknown)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can delete variables (or rather make Python forget about it)\n", "by using the `del` statement." ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "del number" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Different kinds of values, need to be stored differently in memory, which is the reason why we for example distinguish between floats and integers that take different amounts of space in memory. In Python we have the luxury that the kind of the value can be figured out by Python itself. In many other programming languages you would need to state the kind (*declare* the variable) before you could use a variable." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "\n", "**Advanced:** In Python we __define__ variables, meaning we directly assign a value to them, when we create them. It is not possible to just __declare__ a variable without __defining__. This can be very useful. In other languages you can create a variable and the corresponding point to a memory address, without being forced to write a suitable value into this memory address. Calling the variable then returns whatever happens to be stored (e.g. from a previous calculation) at this memory location. This can cause software bugs that are incredibly hard to trace. Instead of __declarations__, Python only knows so-called __type annotations__, that do not reserve memory.\n", "\n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", " \n", "```python\n", ">>> x: int\n", ">>> print(x)\n", "---------------------------------------------------------------------------\n", "NameError Traceback (most recent call last)\n", " in \n", "----> 1 print(x)\n", "\n", "NameError: name 'x' is not defined \n", "\n", "```\n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Objects" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In principle this could be all there is to variables. But Python is a modern object-oriented programming language and everything in Python is an object, including data like numbers. The variable `number` we defined above does not only point to a floating point value in memory, it points to a floating point object that contains a reference to the floating point value besides a bunch of other things. You can think of an object as a bundle of variables (= object *attributes*) and functions (= object *methods*). We can access these attributes via dot-notation. A floating point object has for example an attribute `.real` holding the real part of the floating point number. It might seem silly, but it also has an attribute `.imag` for the imaginary part" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "13.5\n", "0.0\n" ] } ], "source": [ "number = 13.5\n", "\n", "# The floating point object `number` has an attribute `.real`\n", "print(number.real)\n", "# And an attribute `.imag`\n", "print(number.imag)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Floating point objects also come with a handful of useful methods:" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "False" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Is a float a whole number?\n", "number.is_integer()" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "another_number = 15.0\n", "another_number.is_integer()" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(27, 2)" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Return a rational number as a fraction\n", "number.as_integer_ratio()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$13.5 = \\frac{27}{2}$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Other types of objects, come with their own specific set of available attributes." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "\n", "**Note:** Objects carry attributes that can be accessed via `object.attribute` (dot-notation). We can call object methods via `object.method()`.\n", "\n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Of what type is an object?" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You may ask the question now: And how can we know that an object is of a certain kind in Python? The answer is, we can use a function, the function `type()`, that tells us the *type* of an object. How Python deals with types is very flexible. All the more it becomes important that you know about the types you are dealing with." ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n" ] } ], "source": [ "x = \"Good morning, Berlin!\"\n", "print(type(x))" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n" ] } ], "source": [ "number = 13.5\n", "print(type(number))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can say the string object `x` is of type `str` and the float\n", "object `number` is of type `float`. Objects of type string have different attributes than objects of type float." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "When we talk about objects and types in Python, we also come across the term *class*. Let's discuss the difference between a class and an object. You can think of a class as the packing list or the blueprint for a particular object. In Python 3 the *type* of an object and the *class* of an object can be used interchangeably as synonyms. Take this examples:" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n" ] } ], "source": [ "y = 1234\n", "print(type(y))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "By defining the variable `y`, Python automatically recognises that the data value `1234` should be represented as an integer (object of type `int`). It takes the value and bundles it together with everything that makes an integer according to the `int` class (all its attributes) in an object we can now refer to by the variable `y`. We can also say, the new object is an *instance* of the integer class `int`. It is an object build after the building plan given by the class." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "\n", "**Note:** How an object is represented in Python and what you can do with it, is specified by\n", " its __type__ (its __class__). Python can automatically infer the type of objects.\n", "\n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## How do I know which attributes and methods come with an object?" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Sometimes, you may want to know which attributes are currently there to access from an object. This can be achieved with the `dir` function. With that we can get a list of all the attributes attached to an object (don't be alarmed by the length of the list):" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['__add__',\n", " '__class__',\n", " '__contains__',\n", " '__delattr__',\n", " '__dir__',\n", " '__doc__',\n", " '__eq__',\n", " '__format__',\n", " '__ge__',\n", " '__getattribute__',\n", " '__getitem__',\n", " '__getnewargs__',\n", " '__gt__',\n", " '__hash__',\n", " '__init__',\n", " '__init_subclass__',\n", " '__iter__',\n", " '__le__',\n", " '__len__',\n", " '__lt__',\n", " '__mod__',\n", " '__mul__',\n", " '__ne__',\n", " '__new__',\n", " '__reduce__',\n", " '__reduce_ex__',\n", " '__repr__',\n", " '__rmod__',\n", " '__rmul__',\n", " '__setattr__',\n", " '__sizeof__',\n", " '__str__',\n", " '__subclasshook__',\n", " 'capitalize',\n", " 'casefold',\n", " 'center',\n", " 'count',\n", " 'encode',\n", " 'endswith',\n", " 'expandtabs',\n", " 'find',\n", " 'format',\n", " 'format_map',\n", " 'index',\n", " 'isalnum',\n", " 'isalpha',\n", " 'isdecimal',\n", " 'isdigit',\n", " 'isidentifier',\n", " 'islower',\n", " 'isnumeric',\n", " 'isprintable',\n", " 'isspace',\n", " 'istitle',\n", " 'isupper',\n", " 'join',\n", " 'ljust',\n", " 'lower',\n", " 'lstrip',\n", " 'maketrans',\n", " 'partition',\n", " 'replace',\n", " 'rfind',\n", " 'rindex',\n", " 'rjust',\n", " 'rpartition',\n", " 'rsplit',\n", " 'rstrip',\n", " 'split',\n", " 'splitlines',\n", " 'startswith',\n", " 'strip',\n", " 'swapcase',\n", " 'title',\n", " 'translate',\n", " 'upper',\n", " 'zfill']" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "dir(x)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can see that a string object like `x` has a rather large amount of attributes attached to it. The `dir()` function gives you the super power to discover all the available attributes." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## How do I know what all these attributes and methods do?" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To get more information on an objects and its attributes, use the `help` function.\n", "It is particularly helpful, when used on function objects like object methods." ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Help on built-in function is_integer:\n", "\n", "is_integer(...) method of builtins.float instance\n", " Return True if the float is an integer.\n", "\n" ] } ], "source": [ "help(number.is_integer)" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Help on built-in function lower:\n", "\n", "lower(...) method of builtins.str instance\n", " S.lower() -> str\n", " \n", " Return a copy of the string S converted to lowercase.\n", "\n" ] } ], "source": [ "help(x.lower)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This will print in-depth background information on what you have passed as\n", "an argument. Note that we passed in only the name of a function to `help()`. We did *not call* the function we wanted to know more about. It tells you, how you can use this object, which can be incredible\n", "valuable, when you are in doubt about what to do with it." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "\n", "**Note:** Use `help()` to get a helpful description of objects (especially functions) in Python.\n", "\n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Don't be scared of, by the way, by the strange looking elements in the list with the double underscores before and after the attribute name, like `__str__()`. These are special methods, we normally do not need to call directly, but have a special Python-internal meaning in different situations." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## How do I know which objects are currently available?" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can also call the `dir()` function without an explicit argument:" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['In',\n", " 'Out',\n", " '_',\n", " '_12',\n", " '_6',\n", " '_7',\n", " '_8',\n", " '__',\n", " '___',\n", " '__builtin__',\n", " '__builtins__',\n", " '__doc__',\n", " '__loader__',\n", " '__name__',\n", " '__package__',\n", " '__spec__',\n", " '_dh',\n", " '_i',\n", " '_i1',\n", " '_i10',\n", " '_i11',\n", " '_i12',\n", " '_i13',\n", " '_i14',\n", " '_i15',\n", " '_i2',\n", " '_i3',\n", " '_i4',\n", " '_i5',\n", " '_i6',\n", " '_i7',\n", " '_i8',\n", " '_i9',\n", " '_ih',\n", " '_ii',\n", " '_iii',\n", " '_oh',\n", " 'another_number',\n", " 'exit',\n", " 'get_ipython',\n", " 'number',\n", " 'quit',\n", " 'x',\n", " 'y']" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "dir()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The call of `dir()` returns a list of all the objects defined right now\n", "in the current Python session. Besides a lot of weird elements you do not need to care about, we can find in this list the element `'number'`, referring to the object `number`, and others that we have\n", "defined e.g. in the section [Variables](#sec:Variables)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "\n", "**Note:** Use `dir()` to investigate what variables are defined in the current scope/session or associated to a specific object.\n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In the current session, many more objects are available that we have not defined ourself. The functions we have used so far, like `print`, are so-called builtin\n", "functions. Builtin functions are always defined in a standard Python session,\n", "as indicated by the element `'__builtin__'` in the above list we get from `dir()`. \n", "Calling `dir` on the `__builtin__` object, reveals the `print` function among\n", "many other builtin objects." ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['ArithmeticError',\n", " 'AssertionError',\n", " 'AttributeError',\n", " 'BaseException',\n", " 'BlockingIOError',\n", " 'BrokenPipeError',\n", " 'BufferError',\n", " 'BytesWarning',\n", " 'ChildProcessError',\n", " 'ConnectionAbortedError',\n", " 'ConnectionError',\n", " 'ConnectionRefusedError',\n", " 'ConnectionResetError',\n", " 'DeprecationWarning',\n", " 'EOFError',\n", " 'Ellipsis',\n", " 'EnvironmentError',\n", " 'Exception',\n", " 'False',\n", " 'FileExistsError',\n", " 'FileNotFoundError',\n", " 'FloatingPointError',\n", " 'FutureWarning',\n", " 'GeneratorExit',\n", " 'IOError',\n", " 'ImportError',\n", " 'ImportWarning',\n", " 'IndentationError',\n", " 'IndexError',\n", " 'InterruptedError',\n", " 'IsADirectoryError',\n", " 'KeyError',\n", " 'KeyboardInterrupt',\n", " 'LookupError',\n", " 'MemoryError',\n", " 'ModuleNotFoundError',\n", " 'NameError',\n", " 'None',\n", " 'NotADirectoryError',\n", " 'NotImplemented',\n", " 'NotImplementedError',\n", " 'OSError',\n", " 'OverflowError',\n", " 'PendingDeprecationWarning',\n", " 'PermissionError',\n", " 'ProcessLookupError',\n", " 'RecursionError',\n", " 'ReferenceError',\n", " 'ResourceWarning',\n", " 'RuntimeError',\n", " 'RuntimeWarning',\n", " 'StopAsyncIteration',\n", " 'StopIteration',\n", " 'SyntaxError',\n", " 'SyntaxWarning',\n", " 'SystemError',\n", " 'SystemExit',\n", " 'TabError',\n", " 'TimeoutError',\n", " 'True',\n", " 'TypeError',\n", " 'UnboundLocalError',\n", " 'UnicodeDecodeError',\n", " 'UnicodeEncodeError',\n", " 'UnicodeError',\n", " 'UnicodeTranslateError',\n", " 'UnicodeWarning',\n", " 'UserWarning',\n", " 'ValueError',\n", " 'Warning',\n", " 'ZeroDivisionError',\n", " '__IPYTHON__',\n", " '__build_class__',\n", " '__debug__',\n", " '__doc__',\n", " '__import__',\n", " '__loader__',\n", " '__name__',\n", " '__package__',\n", " '__spec__',\n", " 'abs',\n", " 'all',\n", " 'any',\n", " 'ascii',\n", " 'bin',\n", " 'bool',\n", " 'bytearray',\n", " 'bytes',\n", " 'callable',\n", " 'chr',\n", " 'classmethod',\n", " 'compile',\n", " 'complex',\n", " 'copyright',\n", " 'credits',\n", " 'delattr',\n", " 'dict',\n", " 'dir',\n", " 'display',\n", " 'divmod',\n", " 'enumerate',\n", " 'eval',\n", " 'exec',\n", " 'filter',\n", " 'float',\n", " 'format',\n", " 'frozenset',\n", " 'get_ipython',\n", " 'getattr',\n", " 'globals',\n", " 'hasattr',\n", " 'hash',\n", " 'help',\n", " 'hex',\n", " 'id',\n", " 'input',\n", " 'int',\n", " 'isinstance',\n", " 'issubclass',\n", " 'iter',\n", " 'len',\n", " 'license',\n", " 'list',\n", " 'locals',\n", " 'map',\n", " 'max',\n", " 'memoryview',\n", " 'min',\n", " 'next',\n", " 'object',\n", " 'oct',\n", " 'open',\n", " 'ord',\n", " 'pow',\n", " 'print',\n", " 'property',\n", " 'range',\n", " 'repr',\n", " 'reversed',\n", " 'round',\n", " 'set',\n", " 'setattr',\n", " 'slice',\n", " 'sorted',\n", " 'staticmethod',\n", " 'str',\n", " 'sum',\n", " 'super',\n", " 'tuple',\n", " 'type',\n", " 'vars',\n", " 'zip']" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "dir(__builtin__)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "\n", "**Advanced:** The __\\_\\_builtin\\_\\___ object is a so-called *module* in Python. It is\n", " a place in which certain things, like functions, are defined. The module is loaded\n", " (*imported*), when you start your Python session.\n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Remember that you can use `help()` to learn more about a built-in function." ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Help on built-in function print in module builtins:\n", "\n", "print(...)\n", " print(value, ..., sep=' ', end='\\n', file=sys.stdout, flush=False)\n", " \n", " Prints the values to a stream, or to sys.stdout by default.\n", " Optional keyword arguments:\n", " file: a file-like object (stream); defaults to the current sys.stdout.\n", " sep: string inserted between values, default a space.\n", " end: string appended after the last value, default a newline.\n", " flush: whether to forcibly flush the stream.\n", "\n" ] } ], "source": [ "help(print)" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Help on built-in function dir in module builtins:\n", "\n", "dir(...)\n", " dir([object]) -> list of strings\n", " \n", " If called without an argument, return the names in the current scope.\n", " Else, return an alphabetized list of names comprising (some of) the attributes\n", " of the given object, and of attributes reachable from it.\n", " If the object supplies a method named __dir__, it will be used; otherwise\n", " the default dir() logic is used and returns:\n", " for a module object: the module's attributes.\n", " for a class object: its attributes, and recursively the attributes\n", " of its bases.\n", " for any other object: its attributes, its class's attributes, and\n", " recursively the attributes of its class's base classes.\n", "\n" ] } ], "source": [ "help(dir)" ] } ], "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.6.10" }, "toc": { "base_numbering": 1, "nav_menu": {}, "number_sections": true, "sideBar": true, "skip_h1_title": true, "title_cell": "Table of Contents", "title_sidebar": "Contents", "toc_cell": false, "toc_position": { "height": "calc(100% - 180px)", "left": "10px", "top": "150px", "width": "263.6px" }, "toc_section_display": true, "toc_window_display": false }, "varInspector": { "cols": { "lenName": 16, "lenType": 16, "lenVar": 40 }, "kernels_config": { "python": { "delete_cmd_postfix": "", "delete_cmd_prefix": "del ", "library": "var_list.py", "varRefreshCmd": "print(var_dic_list())" }, "r": { "delete_cmd_postfix": ") ", "delete_cmd_prefix": "rm(", "library": "var_list.r", "varRefreshCmd": "cat(var_dic_list()) " } }, "types_to_exclude": [ "module", "function", "builtin_function_or_method", "instance", "_Feature" ], "window_display": false } }, "nbformat": 4, "nbformat_minor": 2 }