π Python Π΄Π»Ρ Π»ΡΠ΄Π΅ΠΉ ΠΈ ΠΈΠ½ΠΎΠΏΠ»Π°Π½Π΅ΡΡΠ½ π
ΠΡΠΎ ΠΌΠ΅ΡΡΠΎ, Π³Π΄Π΅ Ρ Π±ΡΠ΄Ρ Π΄Π΅Π»ΠΈΡΡΡΡ ΠΏΡΠ°ΠΊΡΠΈΡΠ΅ΡΠΊΠΈΠΌΠΈ Π½ΠΎΠ²ΠΈΠ½ΠΊΠ°ΠΌΠΈ Π½ΠΎΠ²ΠΎΠ³ΠΎ ΠΏΠΈΡΠΎΠ½Π° π
ΠΊΠ°ΠΊ Π·Π°ΡΡΠ°Π²ΠΈΡΡ Π²Π°Ρ ΠΊΠΎΠ΄ ΡΠΈΡΡΡ π
ΠΏΠΎΡΡΠΎΠΌΡ, ΠΏΠΎΠΆΠ°Π»ΡΠΉΡΡΠ°, ΠΎΠ±Π½ΠΎΠ²Π»ΡΠΉΡΠ΅ΡΡ ΠΊΠ°ΠΆΠ΄ΡΡ Π½Π΅Π΄Π΅Π»Ρ, Ρ ΠΏΠΎΠ΄Π΅Π»ΡΡΡ ΡΠ΅ΠΌ-Π½ΠΈΠ±ΡΠ΄Ρ ΠΈΠ½ΡΠ΅ΡΠ΅ΡΠ½ΡΠΌ ΠΈ ΠΏΠ»ΠΎΡ ΠΎ ΠΎΠ±Π½ΠΎΠ²Π»Ρ ΡΡΡ ΠΈΡΡΠΎΡΠΈΡ :)
ΠΠΎΠ½ΠΈΠΌΠ°Π½ΠΈΠ΅ ΡΠΏΠΈΡΠΊΠΎΠ² Π² Python Π²Π΅Π»ΠΈΠΊΠΎΠ»Π΅ΠΏΠ½ΠΎ
# Python's list comprehensions are awesome. vals = [expression for value in collection if condition] # This is equivalent to: vals = [] for value in collection: if condition: vals.append(expression) # Example: >>> even_squares = [x * x for x in range(10) if not x % 2] >>> even_squares [0, 4, 16, 36, 64]
Π‘Π»ΠΎΠ²Π° ΠΌΠΎΠΆΠ½ΠΎ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΡ Π΄Π»Ρ ΡΠΌΡΠ»ΡΡΠΈΠΈ ΠΎΠΏΠ΅ΡΠ°ΡΠΎΡΠΎΠ² switch/case
# Because Python has first-class functions they can # be used to emulate switch/case statements def dispatch_if(operator, x, y): if operator == 'add': return x + y elif operator == 'sub': return x - y elif operator == 'mul': return x * y elif operator == 'div': return x / y else: return None def dispatch_dict(operator, x, y): return { 'add': lambda: x + y, 'sub': lambda: x - y, 'mul': lambda: x * y, 'div': lambda: x / y, }.get(operator, lambda: None)() >>> dispatch_if('mul', 2, 8) 16 >>> dispatch_dict('mul', 2, 8) 16 >>> dispatch_if('unknown', 2, 8) None >>> dispatch_dict('unknown', 2, 8) None # Python 3.5+ supports 'type annotations' that can be # used with tools like Mypy to write statically typed Python: def my_add(a: int, b: int) -> int: return a + b >>> lst = [1, 2, 3, 4, 5] >>> del lst[:] >>> lst [] # You can replace all elements of a list # without creating a new list object: >>> a = lst >>> lst[:] = [7, 8, 9] >>> lst [7, 8, 9] >>> a [7, 8, 9] >>> a is lst True # You can also create a (shallow) copy of a list: >>> b = lst[:] >>> b [7, 8, 9] >>> b is lst False >>> import itertools >>> for p in itertools.permutations('ABCD'): ... print(p) ('A', 'B', 'C', 'D') ('A', 'B', 'D', 'C') ('A', 'C', 'B', 'D') ('A', 'C', 'D', 'B') ('A', 'D', 'B', 'C') ('A', 'D', 'C', 'B') ('B', 'A', 'C', 'D') ('B', 'A', 'D', 'C') ('B', 'C', 'A', 'D') ('B', 'C', 'D', 'A') ('B', 'D', 'A', 'C') ('B', 'D', 'C', 'A') ('C', 'A', 'B', 'D') ('C', 'A', 'D', 'B') ('C', 'B', 'A', 'D') ('C', 'B', 'D', 'A') ('C', 'D', 'A', 'B') ('C', 'D', 'B', 'A') ('D', 'A', 'B', 'C') ('D', 'A', 'C', 'B') ('D', 'B', 'A', 'C') ('D', 'B', 'C', 'A') ('D', 'C', 'A', 'B') ('D', 'C', 'B', 'A') # elements in an iterable: >>> import collections >>> c = collections.Counter('helloworld') >>> c Counter({'l': 3, 'o': 2, 'e': 1, 'd': 1, 'h': 1, 'r': 1, 'w': 1}) >>> c.most_common(3) [('l', 3), ('o', 2), ('e', 1)] >>> import datetime >>> today = datetime.date.today() # Result of __str__ should be readable: >>> str(today) '2017-02-02' # Result of __repr__ should be unambiguous: >>> repr(today) 'datetime.date(2017, 2, 2)' # Python interpreter sessions use # __repr__ to inspect objects: >>> today datetime.date(2017, 2, 2) # You can use Python's built-in "dis" # module to disassemble functions and # inspect their CPython VM bytecode: >>> def greet(name): ... return 'Hello, ' + name + '!' >>> greet('Dan') 'Hello, Dan!' >>> import dis >>> dis.dis(greet) 2 0 LOAD_CONST 1 ('Hello, ') 2 LOAD_FAST 0 (name) 4 BINARY_ADD 6 LOAD_CONST 2 ('!') 8 BINARY_ADD 10 RETURN_VALUE # @classmethod vs @staticmethod vs "plain" methods # What's the difference? class MyClass: def method(self): """ Instance methods need a class instance and can access the instance through `self`. """ return 'instance method called', self @classmethod def classmethod(cls): """ Class methods don't need a class instance. They can't access the instance (self) but they have access to the class itself via `cls`. """ return 'class method called', cls @staticmethod def staticmethod(): """ Static methods don't have access to `cls` or `self`. They work like regular functions but belong to the class's namespace. """ return 'static method called' # All methods types can be # called on a class instance: >>> obj = MyClass() >>> obj.method() ('instance method called', <MyClass instance at 0x1019381b8>) >>> obj.classmethod() ('class method called', <class MyClass at 0x101a2f4c8>) >>> obj.staticmethod() 'static method called' # Calling instance methods fails # if we only have the class object: >>> MyClass.classmethod() ('class method called', <class MyClass at 0x101a2f4c8>) >>> MyClass.staticmethod() 'static method called' >>> MyClass.method() TypeError: "unbound method method() must be called with MyClass " "instance as first argument (got nothing instead)" # The lambda keyword in Python provides a # shortcut for declaring small and # anonymous functions: >>> add = lambda x, y: x + y >>> add(5, 3) 8 # You could declare the same add() # function with the def keyword: >>> def add(x, y): ... return x + y >>> add(5, 3) 8 # So what's the big fuss about? # Lambdas are *function expressions*: >>> (lambda x, y: x + y)(5, 3) 8 # β’ Lambda functions are single-expression # functions that are not necessarily bound # to a name (they can be anonymous). # β’ Lambda functions can't use regular # Python statements and always include an # implicit `return` statement. # Python 3 has a std lib # module for working with # IP addresses: >>> import ipaddress >>> ipaddress.ip_address('192.168.1.2') IPv4Address('192.168.1.2') >>> ipaddress.ip_address('2001:af3::') IPv6Address('2001:af3::') # Learn more here: # https://docs.python.org/3/library/ipaddress.html # You can get the name of # an object's class as a # string: >>> class MyClass: pass >>> obj = MyClass() >>> obj.__class__.__name__ 'MyClass' # Functions have a # similar feature: >>> def myfunc(): pass >>> myfunc.__name__ 'myfunc' >>> class BaseClass: pass >>> class SubClass(BaseClass): pass >>> issubclass(SubClass, BaseClass) True >>> issubclass(SubClass, object) True >>> issubclass(BaseClass, SubClass) False >>> Ο = math.pi >>> class SpinΜalTap: pass >>> SpinΜalTap() <SpinΜalTap object at 0x10e58d908> # Only letter-like characters # work, however: >>> πΊ = "beer" SyntaxError: "invalid character in identifier" # Python 3.3+ has a std # lib module for displaying # tracebacks even when Python # "dies", e.g with a segfault: import faulthandler faulthandler.enable() # Can also be enabled with # "python -X faulthandler" # from the command line. # Learn more here: # https://docs.python.org/3/library/faulthandler.html # Virtual Environments ("virtualenvs") keep # your project dependencies separated. # They help you avoid version conflicts # between packages and different versions # of the Python runtime. # Before creating & activating a virtualenv: # `python` and `pip` map to the system # version of the Python interpreter # (e.g. Python 2.7) $ which python /usr/local/bin/python # Let's create a fresh virtualenv using # another version of Python (Python 3): $ python3 -m venv ./venv # A virtualenv is just a "Python # environment in a folder": $ ls ./venv bin include lib pyvenv.cfg # Activating a virtualenv configures the # current shell session to use the python # (and pip) commands from the virtualenv # folder instead of the global environment: $ source ./venv/bin/activate # Note how activating a virtualenv modifies # your shell prompt with a little note # showing the name of the virtualenv folder: (venv) $ echo "wee!" # With an active virtualenv, the `python` # command maps to the interpreter binary # *inside the active virtualenv*: (venv) $ which python /Users/dan/my-project/venv/bin/python3 # Installing new libraries and frameworks # with `pip` now installs them *into the # virtualenv sandbox*, leaving your global # environment (and any other virtualenvs) # completely unmodified: (venv) $ pip install requests # To get back to the global Python # environment, run the following command: (venv) $ deactivate # (See how the prompt changed back # to "normal" again?) $ echo "yay!" # Deactivating the virtualenv flipped the # `python` and `pip` commands back to # the global environment: $ which python /usr/local/bin/python # Python's `for` and `while` loops # support an `else` clause that executes # only if the loops terminates without # hitting a `break` statement. def contains(haystack, needle): """ Throw a ValueError if `needle` not in `haystack`. """ for item in haystack: if item == needle: break else: # The `else` here is a # "completion clause" that runs # only if the loop ran to completion # without hitting a `break` statement. raise ValueError('Needle not found') >>> contains([23, 'needle', 0xbadc0ffee], 'needle') None >>> contains([23, 42, 0xbadc0ffee], 'needle') ValueError: "Needle not found" # Personally, I'm not a fan of the `else` # "completion clause" in loops because # I find it confusing. I'd rather do # something like this: def better_contains(haystack, needle): for item in haystack: if item == needle: return raise ValueError('Needle not found') # Note: Typically you'd write something # like this to do a membership test, # which is much more Pythonic: if needle not in haystack: raise ValueError('Needle not found') # Pythonic ways of checking if all # items in a list are equal: >>> lst = ['a', 'a', 'a'] >>> len(set(lst)) == 1 True >>> all(x == lst[0] for x in lst) True >>> lst.count(lst[0]) == len(lst) True # I ordered those from "most Pythonic" to "least Pythonic" # and "least efficient" to "most efficient". # The len(set()) solution is idiomatic, but constructing # a set is less efficient memory and speed-wise. # In Python 3.4+ you can use # contextlib.suppress() to selectively # ignore specific exceptions: import contextlib with contextlib.suppress(FileNotFoundError): os.remove('somefile.tmp') # This is equivalent to: try: os.remove('somefile.tmp') except FileNotFoundError: pass # contextlib.suppress docstring: # # "Return a context manager that suppresses any # of the specified exceptions if they occur in the body # of a with statement and then resumes execution with # the first statement following the end of # the with statement." # In Python 3 you can use a bare "*" asterisk # in function parameter lists to force the # caller to use keyword arguments for certain # parameters: >>> def f(a, b, *, c='x', d='y', e='z'): ... return 'Hello' # To pass the value for c, d, and e you # will need to explicitly pass it as # "key=value" named arguments: >>> f(1, 2, 'p', 'q', 'v') TypeError: "f() takes 2 positional arguments but 5 were given" >>> f(1, 2, c='p', d='q',e='v') 'Hello' # Python's `for` and `while` loops # support an `else` clause that executes # only if the loops terminates without # hitting a `break` statement. def contains(haystack, needle): """ Throw a ValueError if `needle` not in `haystack`. """ for item in haystack: if item == needle: break else: # The `else` here is a # "completion clause" that runs # only if the loop ran to completion # without hitting a `break` statement. raise ValueError('Needle not found') >>> contains([23, 'needle', 0xbadc0ffee], 'needle') None >>> contains([23, 42, 0xbadc0ffee], 'needle') ValueError: "Needle not found" # Personally, I'm not a fan of the `else` # "completion clause" in loops because # I find it confusing. I'd rather do # something like this: def better_contains(haystack, needle): for item in haystack: if item == needle: return raise ValueError('Needle not found') # Note: Typically you'd write something # like this to do a membership test, # which is much more Pythonic: if needle not in haystack: raise ValueError('Needle not found') # Python 3.5+ allows passing multiple sets # of keyword arguments ("kwargs") to a # function within a single call, using # the "**" syntax: >>> def process_data(a, b, c, d): >>> print(a, b, c, d) >>> x = {'a': 1, 'b': 2} >>> y = {'c': 3, 'd': 4} >>> process_data(**x, **y) 1 2 3 4 >>> process_data(**x, c=23, d=42) 1 2 23 42