Języki skryptowe Python

Wykład 6

Nazwy zmiennych


zmienna = 10              # zmienna wskazuje na int

print(zmienna)

zmienna = "Hello World!"  # zmienna wskazuje na str

print(zmienna)

def funkcja():
    print("Jestem funkcją.")
    
zmienna = funkcja         # zmienna wskazuje na funkcje

zmienna()
10
Hello World!
Jestem funkcją.

Niebezpieczne nadpisania


def moja_nazwa():  # moja_nazwa wskazuje na funkcję
    print("Jestem funkcją.")
    
def moja_nazwa():  # moja_nazwa wskazuje inną funkcję
    print("Jestem nową funkcją.")
    
moja_nazwa()
Jestem nową funkcją.
moja_nazwa = 10  # moja_nazwa wskazuje na int 

moja_nazwa()     # TypeError: 'int' object is not callable
...
TypeError: 'int' object is not callable

Moduły - import ...


import math

def sin(x):
    """Zwraca x."""
    return x
math.sin(math.pi / 2)  # wywołanie [moduł].[funkcja]
1.0
sin(math.pi / 2)  # wywołanie [funkcja]
1.5707963267948966

Moduły - from ... import ...


from math import sin

def sin(x):       # nadpisuje sin z math
    """Zwraca x."""
    return x

sin(math.pi / 2)  # wywołanie funkcji sin
1.5707963267948966
def sin(x):
    """Zwraca x."""
    return x

from math import sin  # nadpisuje sin

sin(math.pi / 2)      # wywołanie math.sin
1.0

Przestrzenie nazw


  • abstrakcyjna przestrzeń przechowująca nazwy
  • np. przestrzeń nazw wbudowanych
print(dir(__builtin__)[-72:])
['abs', 'all', 'any', 'ascii', 'bin', 'bool', 'bytearray', 'bytes', 'callable', 'chr', 'classmethod', 'compile', 'complex', 'copyright', 'credits', 'delattr', 'dict', 'dir', 'divmod', 'dreload', 'enumerate', 'eval', 'exec', 'filter', 'float', 'format', 'frozenset', 'get_ipython', 'getattr', 'globals', 'hasattr', 'hash', 'help', 'hex', 'id', 'input', 'int', 'isinstance', 'issubclass', 'iter', 'len', 'license', 'list', 'locals', 'map', 'max', 'memoryview', 'min', 'next', 'object', 'oct', 'open', 'ord', 'pow', 'print', 'property', 'range', 'repr', 'reversed', 'round', 'set', 'setattr', 'slice', 'sorted', 'staticmethod', 'str', 'sum', 'super', 'tuple', 'type', 'vars', 'zip']

Uwaga: nadpisać można wszystko


slownik = dict(x=1, y=2)

print(slownik)
{'y': 2, 'x': 1}
def dict(x, y):  # nazwy wbudowane też można nadpisać
    return x, y

slownik = dict(x=1, y=2)

print(slownik)
(1, 2)

Przestrzeń nazw lokalnych


  • zmienne zdefiniowane wewnątrz funkcji (niedostępne poza nią)
def funkcja():              # zmienne lokalne dostępne
    zmienna_lokalna = 1     # są tylko wewnątrz funkcji
    return zmienna_lokalna  # w której zostały zdefiniowane

print("zmienna_lokalna =", zmienna_lokalna)
...
NameError: name 'zmienna_lokalna' is not defined

Przestrzeń nazw globalnych


  • dostępne w całym module (pliku)
zmienna_globalna = "Python"

def funkcja():
    return zmienna_globalna

# wewnątrz funkcji zmienne globalne są dostępne
funkcja()
'Python'

Zmienne lokalne nadpisują globalne


zmienna_globalna = "Python"

def funkcja():
    zmienna_globalna = "Nowy Python"         # lokalna zmienna_globalna
    print("in funkcja:", zmienna_globalna)

funkcja()

print("outside funkcja:", zmienna_globalna)  # globalna bez zmian
in funkcja: Nowy Python
outside funkcja: Python

Albo global albo local


zmienna_globalna = "Python"

def funkcja():
    print("in funkcja:", zmienna_globalna)  # globalna?
    zmienna_globalna = "Nowy Python"
    print("in funkcja:", zmienna_globalna)  # lokalna?

funkcja()
...
UnboundLocalError: local variable 'zmienna_globalna' referenced before assignment

Kolejność przestrzeni


#nazwy lokalne, potem globalne, na końcu wbudowane
list = tuple  # zmienna globalna nadpisuje wbudowane list
dict = float  # zmienna globalna nadpisuje wbudowane dict

print("type(list()) =", type(list()))
print("type(dict()) =", type(dict()))

def funkcja():
    dict = int  # zmienna lokalna nadpisuje globalne dict
    print("In funkcja:")
    print("\ttype(dict()) =", type(dict()))  # lokalna
    print("\ttype(list()) =", type(list()))  # globalna
    print("\ttype(int()) =", type(int()))    # wbudowana

funkcja()
type(list()) = <class 'tuple'>
type(dict()) = <class 'float'>
In funkcja:
    type(dict()) = <class 'int'>
    type(list()) = <class 'tuple'>
    type(int()) = <class 'int'>

Wielokrotne zagnieżdżenie


a = "global a"  # zasięg zmiennych a, b, c
b = "global b"  # jest globalny
c = "global c"

def funkcja():
    a = "local a"  # lokalne a, b dostępne w funkcji
    b = "local b"  # i w każdym kolejnym zagnieżdzeniu
    
    def funkcja_w_funkcji():
        a = "local local a"  # dostępna tylko w funkcja_w_funkcji
        print(a, b, c, sep='\n')
    
    funkcja_w_funkcji()
    
funkcja()  # od "najlokalniejszej" do "najglobalniejszej"
local local a
local b
global c

Uwaga na globalne mutowalne


x = [1, 2, 3]
y = ['a', 'b', 'c']

def funkcja():
    x = [1, 2, 3, 4]  # przypisanie -> zmienna lokalna
    y.append('d')     # modyfikacja -> ciągle globalna
    
funkcja()

print(x, y, sep='\n')
[1, 2, 3]
['a', 'b', 'c', 'd']

Wymuszanie zmiennej globalnej


zmienna = "globalna"

def f():
    global zmienna             # przypisanie nie tworzy zmiennej lokalnej
    zmienna = "nowa globalna"  # ale modyfikuje globalną
    
f()

print(zmienna)
nowa globalna

Wymuszanie zmiennej "nielokalnej"


a = "global a"  # globalna

def f():
    a = "local a"  # lokalna w f
    
    def g():
        a = "local local a"  # lokalna w g
        print("in g():", a)
        
    g()
        
    print("in f():", a)

f()

print("outside:", a)
in g(): local local a
in f(): local a
outside: global a

Wymuszanie zmiennej "nielokalnej"


a = "global a"  # globalna

def f():
    a = "local a"  # lokalna w f
    
    def g():
        global a             # używaj globalnej
        a = "local local a"  # modyfikuje globalną
        print("in g():", a)
        
    g()
        
    print("in f():", a)

f()

print("outside:", a)
in g(): local local a
in f(): local a
outside: local local a

Wymuszanie zmiennej "nielokalnej"


a = "global a"  # globalna

def f():
    a = "local a"  # lokalna w f
    
    def g():
        nonlocal a           # użyj a z poprzedniego zagnieżdżenia
        a = "local local a"  # modyfikuje a z f
        print("in g():", a)
        
    g()
        
    print("in f():", a)

f()

print("outside:", a)
in g(): local local a
in f(): local local a
outside: global a

Uwaga dla programistów C/C++


def funkcja(flag=True):
    if flag:    # zmienna zdefiniowana w bloku
        x = 10  # jest dostępna poza tym blokiem
    else:
        x = 20
        
    print(x)
funkcja() 
10
funkcja(False)
20

Zasięg


  • obszar dostępności danej przestrzeni nazw
import math
from math import cos

a = 1  # zasięg -> cały plik

def f():
    # uwaga: zaleca się wszystkie importy robić na początku
    from math import log  # zasięg log(...) -> funkcja
    b = 2                 # zasięg -> funkcja
    c = log(3) 
    
d = math.sin(4)  # sin(...) poza zasięgiem [moduł].[funkcja]
e = cos(5)       # zasięg cos(...) -> cały plik
# f = log(6)     # NameError: name 'log' is not defined

Dokumentacja


help(help)
Help on _Helper in module _sitebuiltins object:

class _Helper(builtins.object)
    |  Define the builtin 'help'.
    |  
    |  This is a wrapper around pydoc.help that provides a helpful message
    |  when 'help' is typed at the Python interactive prompt.
    |  
    |  Calling help() at the Python prompt starts an interactive help session.
    |  Calling help(thing) prints help for the python object 'thing'.
    |  
    |  Methods defined here:
    |  
    |  __call__(self, *args, **kwds)
    |      Call self as a function.
    |  
    |  __repr__(self)
    |      Return repr(self).
    |  
    |  ----------------------------------------------------------------------
    |  Data descriptors defined here:
    |  
    |  __dict__
    |      dictionary for instance variables (if defined)
    |  
    |  __weakref__
    |      list of weak references to the object (if defined)

Dokumentowanie własnych funkcji


def funkcja():
    pass
help(funkcja)
Help on function funkcja in module __main__:

funkcja()
def funkcja():
    """To jest moja funkcja, która nic nie robi"""
    pass
help(funkcja)
Help on function funkcja in module __main__:

funkcja()
    To jest moja funkcja, która nic nie robi

Dokumentacja (PEP 257)


  • dokumentacja (czyli tzw. docstring) wg standardów PEP 257:

    • jednolinjkowy opis
    """Mój opis"""
    • wielolinijkowy opis
    """Mój opis
    
    więcej opisu
    """

Opis


  • opis powinien krótki i jasny (i mieć sens), np:

# taki opis nie ma sensu
def moja_funkcja(a, b):
    """moja_funkcja(a, b) -> a + b"""
    return a + b

# taki jest lepszy
def moja_funkcja(a, b):
    """Zwraca sumę podanych liczb"""
    return a + b

Dokumentowanie zmiennych


def reszta(a = 0, b = 0):
    """Zwraca resztę z dzielenia
    
    Keyword arguments:
    a -- licznik
    b -- mianownik
    """
    if not b:
        return 0
    return a%b
help(reszta)
Help on function reszta in module __main__:

reszta(a=0, b=0)
    Zwraca resztę z dzielenia
    
    Keyword arguments:
    a -- licznik
    b -- mianownik

Wyrażenie lambda


def suma(a, b):  # "klasyczna" funkcja
    return a + b
lsuma = lambda a, b: a + b  # "anonimowa" funkcja
suma(1, 2)  # wywołujemy funkcję suma
3
lsuma(1, 2) # wywołujemy wyrażenia lambda
3

Wyrażenie lambda


def wrapper(funkcja):
    print("Wywołuję funkcję:", funkcja)
    funkcja()

def f():
    print("Hello World!")
    
wrapper(f)
Wywołuję funkcję: <function f at 0x7f92543518c8>
Hello World!
wrapper(lambda: print("Hello World!"))
Wywołuję funkcję: <function <lambda> at 0x7f92543517b8>
Hello World!

Wyrażenie lambda


def increase(n):
    print("Teraz tworzę lambdę")
    return lambda x: x + n

inc2 = increase(2)  # powiększ o 2
inc4 = increase(4)  # inc4 = lambda x: x + 4
Teraz tworzę lambdę
Teraz tworzę lambdę
inc2(10)
12
inc4(10)
14

Rekurencja


i = 0

def funkcja():
    global i  # używaj zmiennej globalnej i
    i += 1
    print(i, end=' ')
    if i < 10:
        return funkcja()  # wywołuje sama siebie
funkcja()
1 2 3 4 5 6 7 8 9 10 

Rekurencja - silnia


def silnia(n):
    result = 1
    while n > 1:
        result *= n
        n -= 1
    return result
print("Klasycznie silnia(5) =", silnia(5))
Klasycznie silnia(5) = 120
def silnia(n):
    if n < 2:
        return 1  # 0! = 1, 1! = 1
    return n*silnia(n - 1)
print("Rekurencyjnie silnia(5) =", silnia(5))
Rekurencyjnie silnia(5) = 120