Как в python инструкции отделяются друг от друга
Python — это высокоуровневый язык программирования, который использует простой, лаконичный синтаксис. Давайте рассмотрим, как в Python отделяются инструкции.
Базовые принципы
В отличие от многих других языков программирования, Python использует новую строку для завершения инструкции. Нет необходимости использовать специальный символ, такой как ;
, который используется в C или Java.
a = 5
b = 6
c = a + b
В приведенном выше примере каждая инструкция находится на новой строке.
Несколько инструкций на одной строке
Хотя это не является общей практикой, в Python можно разделить несколько инструкций на одной строке с помощью символа ;
. Каждый оператор должен быть отделен символом ;
.
a = 5; b = 6; c = a + b
В приведенном выше примере три инструкции разделены символами ;
и расположены на одной строке.
Вложенные инструкции
В Python инструкции могут быть вложенными. В этом случае отступы используются для отделения блоков кода. Каждый уровень вложенности соответствует определенному количеству пробелов или символов табуляции.
if a > b:
print("a больше b")
else:
print("a не больше b")
В приведенном выше примере инструкции внутри блоков if
и else
отделены отступами.
- Author:
- Guido van Rossum <guido at python.org>,
Barry Warsaw <barry at python.org>,
Alyssa Coghlan <ncoghlan at gmail.com> - Status:
- Active
- Type:
- Process
- Created:
- 05-Jul-2001
- Post-History:
- 05-Jul-2001, 01-Aug-2013
- Introduction
- A Foolish Consistency is the Hobgoblin of Little Minds
- Code Lay-out
- Indentation
- Tabs or Spaces?
- Maximum Line Length
- Should a Line Break Before or After a Binary Operator?
- Blank Lines
- Source File Encoding
- Imports
- Module Level Dunder Names
- String Quotes
- Whitespace in Expressions and Statements
- Pet Peeves
- Other Recommendations
- When to Use Trailing Commas
- Comments
- Block Comments
- Inline Comments
- Documentation Strings
- Naming Conventions
- Overriding Principle
- Descriptive: Naming Styles
- Prescriptive: Naming Conventions
- Names to Avoid
- ASCII Compatibility
- Package and Module Names
- Class Names
- Type Variable Names
- Exception Names
- Global Variable Names
- Function and Variable Names
- Function and Method Arguments
- Method Names and Instance Variables
- Constants
- Designing for Inheritance
- Public and Internal Interfaces
- Programming Recommendations
- Function Annotations
- Variable Annotations
- References
- Copyright
Introduction
This document gives coding conventions for the Python code comprising
the standard library in the main Python distribution. Please see the
companion informational PEP describing style guidelines for the C code
in the C implementation of Python.
This document and PEP 257 (Docstring Conventions) were adapted from
Guido’s original Python Style Guide essay, with some additions from
Barry’s style guide [2].
This style guide evolves over time as additional conventions are
identified and past conventions are rendered obsolete by changes in
the language itself.
Many projects have their own coding style guidelines. In the event of any
conflicts, such project-specific guides take precedence for that project.
A Foolish Consistency is the Hobgoblin of Little Minds
One of Guido’s key insights is that code is read much more often than
it is written. The guidelines provided here are intended to improve
the readability of code and make it consistent across the wide
spectrum of Python code. As PEP 20 says, “Readability counts”.
A style guide is about consistency. Consistency with this style guide
is important. Consistency within a project is more important.
Consistency within one module or function is the most important.
However, know when to be inconsistent – sometimes style guide
recommendations just aren’t applicable. When in doubt, use your best
judgment. Look at other examples and decide what looks best. And
don’t hesitate to ask!
In particular: do not break backwards compatibility just to comply with
this PEP!
Some other good reasons to ignore a particular guideline:
- When applying the guideline would make the code less readable, even
for someone who is used to reading code that follows this PEP. - To be consistent with surrounding code that also breaks it (maybe
for historic reasons) – although this is also an opportunity to
clean up someone else’s mess (in true XP style). - Because the code in question predates the introduction of the
guideline and there is no other reason to be modifying that code. - When the code needs to remain compatible with older versions of
Python that don’t support the feature recommended by the style guide.
Code Lay-out
Indentation
Use 4 spaces per indentation level.
Continuation lines should align wrapped elements either vertically
using Python’s implicit line joining inside parentheses, brackets and
braces, or using a hanging indent [1]. When using a hanging
indent the following should be considered; there should be no
arguments on the first line and further indentation should be used to
clearly distinguish itself as a continuation line:
# Correct: # Aligned with opening delimiter. foo = long_function_name(var_one, var_two, var_three, var_four) # Add 4 spaces (an extra level of indentation) to distinguish arguments from the rest. def long_function_name( var_one, var_two, var_three, var_four): print(var_one) # Hanging indents should add a level. foo = long_function_name( var_one, var_two, var_three, var_four)
# Wrong: # Arguments on first line forbidden when not using vertical alignment. foo = long_function_name(var_one, var_two, var_three, var_four) # Further indentation required as indentation is not distinguishable. def long_function_name( var_one, var_two, var_three, var_four): print(var_one)
The 4-space rule is optional for continuation lines.
Optional:
# Hanging indents *may* be indented to other than 4 spaces. foo = long_function_name( var_one, var_two, var_three, var_four)
When the conditional part of an if
-statement is long enough to require
that it be written across multiple lines, it’s worth noting that the
combination of a two character keyword (i.e. if
), plus a single space,
plus an opening parenthesis creates a natural 4-space indent for the
subsequent lines of the multiline conditional. This can produce a visual
conflict with the indented suite of code nested inside the if
-statement,
which would also naturally be indented to 4 spaces. This PEP takes no
explicit position on how (or whether) to further visually distinguish such
conditional lines from the nested suite inside the if
-statement.
Acceptable options in this situation include, but are not limited to:
# No extra indentation. if (this_is_one_thing and that_is_another_thing): do_something() # Add a comment, which will provide some distinction in editors # supporting syntax highlighting. if (this_is_one_thing and that_is_another_thing): # Since both conditions are true, we can frobnicate. do_something() # Add some extra indentation on the conditional continuation line. if (this_is_one_thing and that_is_another_thing): do_something()
(Also see the discussion of whether to break before or after binary
operators below.)
The closing brace/bracket/parenthesis on multiline constructs may
either line up under the first non-whitespace character of the last
line of list, as in:
my_list = [ 1, 2, 3, 4, 5, 6, ] result = some_function_that_takes_arguments( 'a', 'b', 'c', 'd', 'e', 'f', )
or it may be lined up under the first character of the line that
starts the multiline construct, as in:
my_list = [ 1, 2, 3, 4, 5, 6, ] result = some_function_that_takes_arguments( 'a', 'b', 'c', 'd', 'e', 'f', )
Tabs or Spaces?
Spaces are the preferred indentation method.
Tabs should be used solely to remain consistent with code that is
already indented with tabs.
Python disallows mixing tabs and spaces for indentation.
Maximum Line Length
Limit all lines to a maximum of 79 characters.
For flowing long blocks of text with fewer structural restrictions
(docstrings or comments), the line length should be limited to 72
characters.
Limiting the required editor window width makes it possible to have
several files open side by side, and works well when using code
review tools that present the two versions in adjacent columns.
The default wrapping in most tools disrupts the visual structure of the
code, making it more difficult to understand. The limits are chosen to
avoid wrapping in editors with the window width set to 80, even
if the tool places a marker glyph in the final column when wrapping
lines. Some web based tools may not offer dynamic line wrapping at all.
Some teams strongly prefer a longer line length. For code maintained
exclusively or primarily by a team that can reach agreement on this
issue, it is okay to increase the line length limit up to 99 characters,
provided that comments and docstrings are still wrapped at 72
characters.
The Python standard library is conservative and requires limiting
lines to 79 characters (and docstrings/comments to 72).
The preferred way of wrapping long lines is by using Python’s implied
line continuation inside parentheses, brackets and braces. Long lines
can be broken over multiple lines by wrapping expressions in
parentheses. These should be used in preference to using a backslash
for line continuation.
Backslashes may still be appropriate at times. For example, long,
multiple with
-statements could not use implicit continuation
before Python 3.10, so backslashes were acceptable for that case:
with open('/path/to/some/file/you/want/to/read') as file_1, \ open('/path/to/some/file/being/written', 'w') as file_2: file_2.write(file_1.read())
(See the previous discussion on multiline if-statements for further
thoughts on the indentation of such multiline with
-statements.)
Another such case is with assert
statements.
Make sure to indent the continued line appropriately.
Should a Line Break Before or After a Binary Operator?
For decades the recommended style was to break after binary operators.
But this can hurt readability in two ways: the operators tend to get
scattered across different columns on the screen, and each operator is
moved away from its operand and onto the previous line. Here, the eye
has to do extra work to tell which items are added and which are
subtracted:
# Wrong: # operators sit far away from their operands income = (gross_wages + taxable_interest + (dividends - qualified_dividends) - ira_deduction - student_loan_interest)
To solve this readability problem, mathematicians and their publishers
follow the opposite convention. Donald Knuth explains the traditional
rule in his Computers and Typesetting series: “Although formulas
within a paragraph always break after binary operations and relations,
displayed formulas always break before binary operations” [3].
Following the tradition from mathematics usually results in more
readable code:
# Correct: # easy to match operators with operands income = (gross_wages + taxable_interest + (dividends - qualified_dividends) - ira_deduction - student_loan_interest)
In Python code, it is permissible to break before or after a binary
operator, as long as the convention is consistent locally. For new
code Knuth’s style is suggested.
Blank Lines
Surround top-level function and class definitions with two blank
lines.
Method definitions inside a class are surrounded by a single blank
line.
Extra blank lines may be used (sparingly) to separate groups of
related functions. Blank lines may be omitted between a bunch of
related one-liners (e.g. a set of dummy implementations).
Use blank lines in functions, sparingly, to indicate logical sections.
Python accepts the control-L (i.e. ^L) form feed character as
whitespace; many tools treat these characters as page separators, so
you may use them to separate pages of related sections of your file.
Note, some editors and web-based code viewers may not recognize
control-L as a form feed and will show another glyph in its place.
Source File Encoding
Code in the core Python distribution should always use UTF-8, and should not
have an encoding declaration.
In the standard library, non-UTF-8 encodings should be used only for
test purposes. Use non-ASCII characters sparingly, preferably only to
denote places and human names. If using non-ASCII characters as data,
avoid noisy Unicode characters like z̯̯͡a̧͎̺l̡͓̫g̹̲o̡̼̘ and byte order
marks.
All identifiers in the Python standard library MUST use ASCII-only
identifiers, and SHOULD use English words wherever feasible (in many
cases, abbreviations and technical terms are used which aren’t
English).
Open source projects with a global audience are encouraged to adopt a
similar policy.
Imports
- Imports should usually be on separate lines:
# Correct: import os import sys
It’s okay to say this though:
# Correct: from subprocess import Popen, PIPE
- Imports are always put at the top of the file, just after any module
comments and docstrings, and before module globals and constants.Imports should be grouped in the following order:
- Standard library imports.
- Related third party imports.
- Local application/library specific imports.
You should put a blank line between each group of imports.
- Absolute imports are recommended, as they are usually more readable
and tend to be better behaved (or at least give better error
messages) if the import system is incorrectly configured (such as
when a directory inside a package ends up onsys.path
):import mypkg.sibling from mypkg import sibling from mypkg.sibling import example
However, explicit relative imports are an acceptable alternative to
absolute imports, especially when dealing with complex package layouts
where using absolute imports would be unnecessarily verbose:from . import sibling from .sibling import example
Standard library code should avoid complex package layouts and always
use absolute imports. - When importing a class from a class-containing module, it’s usually
okay to spell this:from myclass import MyClass from foo.bar.yourclass import YourClass
If this spelling causes local name clashes, then spell them explicitly:
import myclass import foo.bar.yourclass
and use “myclass.MyClass” and “foo.bar.yourclass.YourClass”.
- Wildcard imports (
from <module> import *
) should be avoided, as
they make it unclear which names are present in the namespace,
confusing both readers and many automated tools. There is one
defensible use case for a wildcard import, which is to republish an
internal interface as part of a public API (for example, overwriting
a pure Python implementation of an interface with the definitions
from an optional accelerator module and exactly which definitions
will be overwritten isn’t known in advance).When republishing names this way, the guidelines below regarding
public and internal interfaces still apply.
Module Level Dunder Names
Module level “dunders” (i.e. names with two leading and two trailing
underscores) such as __all__
, __author__
, __version__
,
etc. should be placed after the module docstring but before any import
statements except from __future__
imports. Python mandates that
future-imports must appear in the module before any other code except
docstrings:
"""This is the example module. This module does stuff. """ from __future__ import barry_as_FLUFL __all__ = ['a', 'b', 'c'] __version__ = '0.1' __author__ = 'Cardinal Biggles' import os import sys
String Quotes
In Python, single-quoted strings and double-quoted strings are the
same. This PEP does not make a recommendation for this. Pick a rule
and stick to it. When a string contains single or double quote
characters, however, use the other one to avoid backslashes in the
string. It improves readability.
For triple-quoted strings, always use double quote characters to be
consistent with the docstring convention in PEP 257.
Whitespace in Expressions and Statements
Pet Peeves
Avoid extraneous whitespace in the following situations:
- Immediately inside parentheses, brackets or braces:
# Correct: spam(ham[1], {eggs: 2})
# Wrong: spam( ham[ 1 ], { eggs: 2 } )
- Between a trailing comma and a following close parenthesis:
- Immediately before a comma, semicolon, or colon:
# Correct: if x == 4: print(x, y); x, y = y, x
# Wrong: if x == 4 : print(x , y) ; x , y = y , x
- However, in a slice the colon acts like a binary operator, and
should have equal amounts on either side (treating it as the
operator with the lowest priority). In an extended slice, both
colons must have the same amount of spacing applied. Exception:
when a slice parameter is omitted, the space is omitted:# Correct: ham[1:9], ham[1:9:3], ham[:9:3], ham[1::3], ham[1:9:] ham[lower:upper], ham[lower:upper:], ham[lower::step] ham[lower+offset : upper+offset] ham[: upper_fn(x) : step_fn(x)], ham[:: step_fn(x)] ham[lower + offset : upper + offset]
# Wrong: ham[lower + offset:upper + offset] ham[1: 9], ham[1 :9], ham[1:9 :3] ham[lower : : step] ham[ : upper]
- Immediately before the open parenthesis that starts the argument
list of a function call: - Immediately before the open parenthesis that starts an indexing or
slicing:# Correct: dct['key'] = lst[index]
# Wrong: dct ['key'] = lst [index]
- More than one space around an assignment (or other) operator to
align it with another:# Correct: x = 1 y = 2 long_variable = 3
# Wrong: x = 1 y = 2 long_variable = 3
Other Recommendations
- Avoid trailing whitespace anywhere. Because it’s usually invisible,
it can be confusing: e.g. a backslash followed by a space and a
newline does not count as a line continuation marker. Some editors
don’t preserve it and many projects (like CPython itself) have
pre-commit hooks that reject it. - Always surround these binary operators with a single space on either
side: assignment (=
), augmented assignment (+=
,-=
etc.), comparisons (==
,<
,>
,!=
,<>
,<=
,
>=
,in
,not in
,is
,is not
), Booleans (and
,
or
,not
). - If operators with different priorities are used, consider adding
whitespace around the operators with the lowest priority(ies). Use
your own judgment; however, never use more than one space, and
always have the same amount of whitespace on both sides of a binary
operator:# Correct: i = i + 1 submitted += 1 x = x*2 - 1 hypot2 = x*x + y*y c = (a+b) * (a-b)
# Wrong: i=i+1 submitted +=1 x = x * 2 - 1 hypot2 = x * x + y * y c = (a + b) * (a - b)
- Function annotations should use the normal rules for colons and
always have spaces around the->
arrow if present. (See
Function Annotations below for more about function annotations.):# Correct: def munge(input: AnyStr): ... def munge() -> PosInt: ...
# Wrong: def munge(input:AnyStr): ... def munge()->PosInt: ...
- Don’t use spaces around the
=
sign when used to indicate a
keyword argument, or when used to indicate a default value for an
unannotated function parameter:# Correct: def complex(real, imag=0.0): return magic(r=real, i=imag)
# Wrong: def complex(real, imag = 0.0): return magic(r = real, i = imag)
When combining an argument annotation with a default value, however, do use
spaces around the=
sign:# Correct: def munge(sep: AnyStr = None): ... def munge(input: AnyStr, sep: AnyStr = None, limit=1000): ...
# Wrong: def munge(input: AnyStr=None): ... def munge(input: AnyStr, limit = 1000): ...
- Compound statements (multiple statements on the same line) are
generally discouraged:# Correct: if foo == 'blah': do_blah_thing() do_one() do_two() do_three()
Rather not:
# Wrong: if foo == 'blah': do_blah_thing() do_one(); do_two(); do_three()
- While sometimes it’s okay to put an if/for/while with a small body
on the same line, never do this for multi-clause statements. Also
avoid folding such long lines!Rather not:
# Wrong: if foo == 'blah': do_blah_thing() for x in lst: total += x while t < 10: t = delay()
Definitely not:
# Wrong: if foo == 'blah': do_blah_thing() else: do_non_blah_thing() try: something() finally: cleanup() do_one(); do_two(); do_three(long, argument, list, like, this) if foo == 'blah': one(); two(); three()
When to Use Trailing Commas
Trailing commas are usually optional, except they are mandatory when
making a tuple of one element. For clarity, it is recommended to
surround the latter in (technically redundant) parentheses:
# Correct: FILES = ('setup.cfg',)
# Wrong: FILES = 'setup.cfg',
When trailing commas are redundant, they are often helpful when a
version control system is used, when a list of values, arguments or
imported items is expected to be extended over time. The pattern is
to put each value (etc.) on a line by itself, always adding a trailing
comma, and add the close parenthesis/bracket/brace on the next line.
However it does not make sense to have a trailing comma on the same
line as the closing delimiter (except in the above case of singleton
tuples):
# Correct: FILES = [ 'setup.cfg', 'tox.ini', ] initialize(FILES, error=True, )
# Wrong: FILES = ['setup.cfg', 'tox.ini',] initialize(FILES, error=True,)
Naming Conventions
The naming conventions of Python’s library are a bit of a mess, so
we’ll never get this completely consistent – nevertheless, here are
the currently recommended naming standards. New modules and packages
(including third party frameworks) should be written to these
standards, but where an existing library has a different style,
internal consistency is preferred.
Overriding Principle
Names that are visible to the user as public parts of the API should
follow conventions that reflect usage rather than implementation.
Descriptive: Naming Styles
There are a lot of different naming styles. It helps to be able to
recognize what naming style is being used, independently from what
they are used for.
The following naming styles are commonly distinguished:
b
(single lowercase letter)B
(single uppercase letter)lowercase
lower_case_with_underscores
UPPERCASE
UPPER_CASE_WITH_UNDERSCORES
CapitalizedWords
(or CapWords, or CamelCase – so named because
of the bumpy look of its letters [4]). This is also sometimes known
as StudlyCaps.Note: When using acronyms in CapWords, capitalize all the
letters of the acronym. Thus HTTPServerError is better than
HttpServerError.mixedCase
(differs from CapitalizedWords by initial lowercase
character!)Capitalized_Words_With_Underscores
(ugly!)
There’s also the style of using a short unique prefix to group related
names together. This is not used much in Python, but it is mentioned
for completeness. For example, the os.stat()
function returns a
tuple whose items traditionally have names like st_mode
,
st_size
, st_mtime
and so on. (This is done to emphasize the
correspondence with the fields of the POSIX system call struct, which
helps programmers familiar with that.)
The X11 library uses a leading X for all its public functions. In
Python, this style is generally deemed unnecessary because attribute
and method names are prefixed with an object, and function names are
prefixed with a module name.
In addition, the following special forms using leading or trailing
underscores are recognized (these can generally be combined with any
case convention):
_single_leading_underscore
: weak “internal use” indicator.
E.g.from M import *
does not import objects whose names start
with an underscore.single_trailing_underscore_
: used by convention to avoid
conflicts with Python keyword, e.g.tkinter.Toplevel(master, class_='ClassName')
__double_leading_underscore
: when naming a class attribute,
invokes name mangling (inside class FooBar,__boo
becomes
_FooBar__boo
; see below).__double_leading_and_trailing_underscore__
: “magic” objects or
attributes that live in user-controlled namespaces.
E.g.__init__
,__import__
or__file__
. Never invent
such names; only use them as documented.
Prescriptive: Naming Conventions
Names to Avoid
Never use the characters ‘l’ (lowercase letter el), ‘O’ (uppercase
letter oh), or ‘I’ (uppercase letter eye) as single character variable
names.
In some fonts, these characters are indistinguishable from the
numerals one and zero. When tempted to use ‘l’, use ‘L’ instead.
ASCII Compatibility
Identifiers used in the standard library must be ASCII compatible
as described in the
policy section
of PEP 3131.
Package and Module Names
Modules should have short, all-lowercase names. Underscores can be
used in the module name if it improves readability. Python packages
should also have short, all-lowercase names, although the use of
underscores is discouraged.
When an extension module written in C or C++ has an accompanying
Python module that provides a higher level (e.g. more object oriented)
interface, the C/C++ module has a leading underscore
(e.g. _socket
).
Class Names
Class names should normally use the CapWords convention.
The naming convention for functions may be used instead in cases where
the interface is documented and used primarily as a callable.
Note that there is a separate convention for builtin names: most builtin
names are single words (or two words run together), with the CapWords
convention used only for exception names and builtin constants.
Type Variable Names
Names of type variables introduced in PEP 484 should normally use CapWords
preferring short names: T
, AnyStr
, Num
. It is recommended to add
suffixes _co
or _contra
to the variables used to declare covariant
or contravariant behavior correspondingly:
from typing import TypeVar VT_co = TypeVar('VT_co', covariant=True) KT_contra = TypeVar('KT_contra', contravariant=True)
Exception Names
Because exceptions should be classes, the class naming convention
applies here. However, you should use the suffix “Error” on your
exception names (if the exception actually is an error).
Global Variable Names
(Let’s hope that these variables are meant for use inside one module
only.) The conventions are about the same as those for functions.
Modules that are designed for use via from M import *
should use
the __all__
mechanism to prevent exporting globals, or use the
older convention of prefixing such globals with an underscore (which
you might want to do to indicate these globals are “module
non-public”).
Function and Variable Names
Function names should be lowercase, with words separated by
underscores as necessary to improve readability.
Variable names follow the same convention as function names.
mixedCase is allowed only in contexts where that’s already the
prevailing style (e.g. threading.py), to retain backwards
compatibility.
Function and Method Arguments
Always use self
for the first argument to instance methods.
Always use cls
for the first argument to class methods.
If a function argument’s name clashes with a reserved keyword, it is
generally better to append a single trailing underscore rather than
use an abbreviation or spelling corruption. Thus class_
is better
than clss
. (Perhaps better is to avoid such clashes by using a
synonym.)
Method Names and Instance Variables
Use the function naming rules: lowercase with words separated by
underscores as necessary to improve readability.
Use one leading underscore only for non-public methods and instance
variables.
To avoid name clashes with subclasses, use two leading underscores to
invoke Python’s name mangling rules.
Python mangles these names with the class name: if class Foo has an
attribute named __a
, it cannot be accessed by Foo.__a
. (An
insistent user could still gain access by calling Foo._Foo__a
.)
Generally, double leading underscores should be used only to avoid
name conflicts with attributes in classes designed to be subclassed.
Note: there is some controversy about the use of __names (see below).
Constants
Constants are usually defined on a module level and written in all
capital letters with underscores separating words. Examples include
MAX_OVERFLOW
and TOTAL
.
Designing for Inheritance
Always decide whether a class’s methods and instance variables
(collectively: “attributes”) should be public or non-public. If in
doubt, choose non-public; it’s easier to make it public later than to
make a public attribute non-public.
Public attributes are those that you expect unrelated clients of your
class to use, with your commitment to avoid backwards incompatible
changes. Non-public attributes are those that are not intended to be
used by third parties; you make no guarantees that non-public
attributes won’t change or even be removed.
We don’t use the term “private” here, since no attribute is really
private in Python (without a generally unnecessary amount of work).
Another category of attributes are those that are part of the
“subclass API” (often called “protected” in other languages). Some
classes are designed to be inherited from, either to extend or modify
aspects of the class’s behavior. When designing such a class, take
care to make explicit decisions about which attributes are public,
which are part of the subclass API, and which are truly only to be
used by your base class.
With this in mind, here are the Pythonic guidelines:
- Public attributes should have no leading underscores.
- If your public attribute name collides with a reserved keyword,
append a single trailing underscore to your attribute name. This is
preferable to an abbreviation or corrupted spelling. (However,
notwithstanding this rule, ‘cls’ is the preferred spelling for any
variable or argument which is known to be a class, especially the
first argument to a class method.)Note 1: See the argument name recommendation above for class methods.
- For simple public data attributes, it is best to expose just the
attribute name, without complicated accessor/mutator methods. Keep
in mind that Python provides an easy path to future enhancement,
should you find that a simple data attribute needs to grow
functional behavior. In that case, use properties to hide
functional implementation behind simple data attribute access
syntax.Note 1: Try to keep the functional behavior side-effect free,
although side-effects such as caching are generally fine.Note 2: Avoid using properties for computationally expensive
operations; the attribute notation makes the caller believe that
access is (relatively) cheap. - If your class is intended to be subclassed, and you have attributes
that you do not want subclasses to use, consider naming them with
double leading underscores and no trailing underscores. This
invokes Python’s name mangling algorithm, where the name of the
class is mangled into the attribute name. This helps avoid
attribute name collisions should subclasses inadvertently contain
attributes with the same name.Note 1: Note that only the simple class name is used in the mangled
name, so if a subclass chooses both the same class name and attribute
name, you can still get name collisions.Note 2: Name mangling can make certain uses, such as debugging and
__getattr__()
, less convenient. However the name mangling
algorithm is well documented and easy to perform manually.Note 3: Not everyone likes name mangling. Try to balance the
need to avoid accidental name clashes with potential use by
advanced callers.
Public and Internal Interfaces
Any backwards compatibility guarantees apply only to public interfaces.
Accordingly, it is important that users be able to clearly distinguish
between public and internal interfaces.
Documented interfaces are considered public, unless the documentation
explicitly declares them to be provisional or internal interfaces exempt
from the usual backwards compatibility guarantees. All undocumented
interfaces should be assumed to be internal.
To better support introspection, modules should explicitly declare the
names in their public API using the __all__
attribute. Setting
__all__
to an empty list indicates that the module has no public API.
Even with __all__
set appropriately, internal interfaces (packages,
modules, classes, functions, attributes or other names) should still be
prefixed with a single leading underscore.
An interface is also considered internal if any containing namespace
(package, module or class) is considered internal.
Imported names should always be considered an implementation detail.
Other modules must not rely on indirect access to such imported names
unless they are an explicitly documented part of the containing module’s
API, such as os.path
or a package’s __init__
module that exposes
functionality from submodules.
Programming Recommendations
- Code should be written in a way that does not disadvantage other
implementations of Python (PyPy, Jython, IronPython, Cython, Psyco,
and such).For example, do not rely on CPython’s efficient implementation of
in-place string concatenation for statements in the forma += b
ora = a + b
. This optimization is fragile even in CPython (it
only works for some types) and isn’t present at all in implementations
that don’t use refcounting. In performance sensitive parts of the
library, the''.join()
form should be used instead. This will
ensure that concatenation occurs in linear time across various
implementations. - Comparisons to singletons like None should always be done with
is
oris not
, never the equality operators.Also, beware of writing
if x
when you really meanif x is not
– e.g. when testing whether a variable or argument that
None
defaults to None was set to some other value. The other value might
have a type (such as a container) that could be false in a boolean
context! - Use
is not
operator rather thannot ... is
. While both
expressions are functionally identical, the former is more readable
and preferred:# Correct: if foo is not None:
# Wrong: if not foo is None:
- When implementing ordering operations with rich comparisons, it is
best to implement all six operations (__eq__
,__ne__
,
__lt__
,__le__
,__gt__
,__ge__
) rather than relying
on other code to only exercise a particular comparison.To minimize the effort involved, the
functools.total_ordering()
decorator provides a tool to generate missing comparison methods.PEP 207 indicates that reflexivity rules are assumed by Python.
Thus, the interpreter may swapy > x
withx < y
,y >= x
withx <= y
, and may swap the arguments ofx == y
andx !=
. The
ysort()
andmin()
operations are guaranteed to use
the<
operator and themax()
function uses the>
operator. However, it is best to implement all six operations so
that confusion doesn’t arise in other contexts. - Always use a def statement instead of an assignment statement that binds
a lambda expression directly to an identifier:# Correct: def f(x): return 2*x
# Wrong: f = lambda x: 2*x
The first form means that the name of the resulting function object is
specifically ‘f’ instead of the generic ‘<lambda>’. This is more
useful for tracebacks and string representations in general. The use
of the assignment statement eliminates the sole benefit a lambda
expression can offer over an explicit def statement (i.e. that it can
be embedded inside a larger expression) - Derive exceptions from
Exception
rather thanBaseException
.
Direct inheritance fromBaseException
is reserved for exceptions
where catching them is almost always the wrong thing to do.Design exception hierarchies based on the distinctions that code
catching the exceptions is likely to need, rather than the locations
where the exceptions are raised. Aim to answer the question
“What went wrong?” programmatically, rather than only stating that
“A problem occurred” (see PEP 3151 for an example of this lesson being
learned for the builtin exception hierarchy)Class naming conventions apply here, although you should add the
suffix “Error” to your exception classes if the exception is an
error. Non-error exceptions that are used for non-local flow control
or other forms of signaling need no special suffix. - Use exception chaining appropriately.
raise X from Y
should be used to indicate explicit replacement without losing the
original traceback.When deliberately replacing an inner exception (using
raise X from
), ensure that relevant details are transferred to the new
None
exception (such as preserving the attribute name when converting
KeyError to AttributeError, or embedding the text of the original
exception in the new exception message). - When catching exceptions, mention specific exceptions whenever
possible instead of using a bareexcept:
clause:try: import platform_specific_module except ImportError: platform_specific_module = None
A bare
except:
clause will catch SystemExit and
KeyboardInterrupt exceptions, making it harder to interrupt a
program with Control-C, and can disguise other problems. If you
want to catch all exceptions that signal program errors, use
except Exception:
(bare except is equivalent toexcept
).
BaseException:A good rule of thumb is to limit use of bare ‘except’ clauses to two
cases:- If the exception handler will be printing out or logging the
traceback; at least the user will be aware that an error has
occurred. - If the code needs to do some cleanup work, but then lets the
exception propagate upwards withraise
.try...finally
can be a better way to handle this case.
- If the exception handler will be printing out or logging the
- When catching operating system errors, prefer the explicit exception
hierarchy introduced in Python 3.3 over introspection oferrno
values. - Additionally, for all try/except clauses, limit the
try
clause
to the absolute minimum amount of code necessary. Again, this
avoids masking bugs:# Correct: try: value = collection[key] except KeyError: return key_not_found(key) else: return handle_value(value)
# Wrong: try: # Too broad! return handle_value(collection[key]) except KeyError: # Will also catch KeyError raised by handle_value() return key_not_found(key)
- When a resource is local to a particular section of code, use a
with
statement to ensure it is cleaned up promptly and reliably
after use. A try/finally statement is also acceptable. - Context managers should be invoked through separate functions or methods
whenever they do something other than acquire and release resources:# Correct: with conn.begin_transaction(): do_stuff_in_transaction(conn)
# Wrong: with conn: do_stuff_in_transaction(conn)
The latter example doesn’t provide any information to indicate that
the__enter__
and__exit__
methods are doing something other
than closing the connection after a transaction. Being explicit is
important in this case. - Be consistent in return statements. Either all return statements in
a function should return an expression, or none of them should. If
any return statement returns an expression, any return statements
where no value is returned should explicitly state this asreturn
, and an explicit return statement should be present at the
None
end of the function (if reachable):# Correct: def foo(x): if x >= 0: return math.sqrt(x) else: return None def bar(x): if x < 0: return None return math.sqrt(x)
# Wrong: def foo(x): if x >= 0: return math.sqrt(x) def bar(x): if x < 0: return return math.sqrt(x)
- Use
''.startswith()
and''.endswith()
instead of string
slicing to check for prefixes or suffixes.startswith() and endswith() are cleaner and less error prone:
# Correct: if foo.startswith('bar'):
# Wrong: if foo[:3] == 'bar':
- Object type comparisons should always use isinstance() instead of
comparing types directly:# Correct: if isinstance(obj, int):
# Wrong: if type(obj) is type(1):
- For sequences, (strings, lists, tuples), use the fact that empty
sequences are false:# Correct: if not seq: if seq:
# Wrong: if len(seq): if not len(seq):
- Don’t write string literals that rely on significant trailing
whitespace. Such trailing whitespace is visually indistinguishable
and some editors (or more recently, reindent.py) will trim them. - Don’t compare boolean values to True or False using
==
:# Wrong: if greeting == True:
Worse:
# Wrong: if greeting is True:
- Use of the flow control statements
return
/break
/continue
within the finally suite of atry...finally
, where the flow control
statement would jump outside the finally suite, is discouraged. This
is because such statements will implicitly cancel any active exception
that is propagating through the finally suite:# Wrong: def foo(): try: 1 / 0 finally: return 42
Function Annotations
With the acceptance of PEP 484, the style rules for function
annotations have changed.
- Function annotations should use PEP 484 syntax (there are some
formatting recommendations for annotations in the previous section). - The experimentation with annotation styles that was recommended
previously in this PEP is no longer encouraged. - However, outside the stdlib, experiments within the rules of PEP 484
are now encouraged. For example, marking up a large third party
library or application with PEP 484 style type annotations,
reviewing how easy it was to add those annotations, and observing
whether their presence increases code understandability. - The Python standard library should be conservative in adopting such
annotations, but their use is allowed for new code and for big
refactorings. - For code that wants to make a different use of function annotations
it is recommended to put a comment of the form:near the top of the file; this tells type checkers to ignore all
annotations. (More fine-grained ways of disabling complaints from
type checkers can be found in PEP 484.) - Like linters, type checkers are optional, separate tools. Python
interpreters by default should not issue any messages due to type
checking and should not alter their behavior based on annotations. - Users who don’t want to use type checkers are free to ignore them.
However, it is expected that users of third party library packages
may want to run type checkers over those packages. For this purpose
PEP 484 recommends the use of stub files: .pyi files that are read
by the type checker in preference of the corresponding .py files.
Stub files can be distributed with a library, or separately (with
the library author’s permission) through the typeshed repo [5].
Variable Annotations
PEP 526 introduced variable annotations. The style recommendations for them are
similar to those on function annotations described above:
- Annotations for module level variables, class and instance variables,
and local variables should have a single space after the colon. - There should be no space before the colon.
- If an assignment has a right hand side, then the equality sign should have
exactly one space on both sides:# Correct: code: int class Point: coords: Tuple[int, int] label: str = '<unknown>'
# Wrong: code:int # No space after colon code : int # Space before colon class Test: result: int=0 # No spaces around equality sign
- Although the PEP 526 is accepted for Python 3.6, the variable annotation
syntax is the preferred syntax for stub files on all versions of Python
(see PEP 484 for details).
Footnotes
References
Copyright
This document has been placed in the public domain.
Когда мы готовим блюдо, то четко следуем рецепту. Иначе еда окажется не такой, как ожидали. Это же правило действует и в программировании.
Чтобы увидеть на экране ожидаемый результат, нужно дать компьютеру четкие и пошаговые указания. Это можно сделать с помощью инструкций. Инструкция — это команда для компьютера, единица выполнения. Код на Python в этом случае — это набор инструкций. Его можно представить в виде пошагового рецепта.
Код на Python запускает интерпретатор — программу, которая выполняет инструкции строго по очереди. Как и шаги в рецепте, набор инструкций для интерпретатора пишутся по порядку и отделяются друг от друга переходом на следующую строку.
Разработчики должны понимать порядок действий в коде и уметь мысленно разделять программу на независимые части, удобные для анализа.
Посмотрим на пример кода с двумя инструкциями. При его запуске на экран последовательно выводятся два предложения:
print('Mother of Dragons.')
print('Dracarys!')
# => Mother of Dragons.
# => Dracarys!
Выше мы говорили, что инструкции отделяются друг от друга переходом на новую строку. Но есть и другой способ: их можно разделить точкой с запятой — ;
:
print('Mother of Dragons.'); print('Drakarys!')
Технической разницы между первым и вторым вариантом нет — интерпретатор поймет инструкции одинаково. Разница только в том, что человеку будет неудобно читать второй вариант.
Лучше инструкции располагать друг под другом. Так коллегам будет удобнее читать ваш код, обслуживать его и вносить изменения.
Задание
Выведите на экран друг за другом три имени: Robert, Stannis, Renly. В результате на экране должно отобразиться:
Robert Stannis Renly
Для каждого имени используйте свой собственный вызов print()
.
Если вы зашли в тупик, то самое время задать вопрос в «Обсуждениях». Как правильно задать вопрос:
- Обязательно приложите вывод тестов, без него практически невозможно понять что не так, даже если вы покажете свой код. Программисты плохо исполняют код в голове, но по полученной ошибке почти всегда понятно, куда смотреть.
В моей среде код работает, а здесь нет 🤨
Тесты устроены таким образом, что они проверяют решение разными способами и на разных данных. Часто решение работает с одними входными данными, но не работает с другими. Чтобы разобраться с этим моментом, изучите вкладку «Тесты» и внимательно посмотрите на вывод ошибок, в котором есть подсказки.
Мой код отличается от решения учителя 🤔
Это нормально 🙆, в программировании одну задачу можно выполнить множеством способов. Если ваш код прошел проверку, то он соответствует условиям задачи.
В редких случаях бывает, что решение подогнано под тесты, но это видно сразу.
Прочитал урок — ничего не понятно 🙄
Создавать обучающие материалы, понятные для всех без исключения, довольно сложно. Мы очень стараемся, но всегда есть что улучшать. Если вы встретили материал, который вам непонятен, опишите проблему в «Обсуждениях». Идеально, если вы сформулируете непонятные моменты в виде вопросов. Обычно нам нужно несколько дней для внесения правок.
Кстати, вы тоже можете участвовать в улучшении курсов: внизу есть ссылка на исходный код уроков, который можно править прямо из браузера.
Полезное
-
Немного об интерпретаторах
Определения
-
Интерпретатор — программа, выполняющая код на Python.
-
Инструкция (statement) — команда для компьютера, написанная на языке программирования. Код на Python — это набор инструкций, разделенных (чаще всего) переводом строки.
Нашли ошибку? Есть что добавить? Пулреквесты приветствуются https://github.com/hexlet-basics
Python: Композиция операций
А что, если понадобится вычислить такое выражение: 3 * 5 - 2
? Именно так мы и запишем:
print(3 * 5 - 2) # => 13
Обратите внимание, что интерпретатор производит арифметические вычисления в правильном порядке: сначала деление и умножение, потом сложение и вычитание. Иногда этот порядок нужно изменить — об этом следующий урок.
Или другой пример:
print(2 * 4 * 5 * 10)
Как видно, операции можно соединять друг с другом, получая возможность вычислять все более сложные составные выражения. Чтобы представить себе то, как происходят вычисления внутри интерпретатора, давайте разберем пример: 2 * 4 * 5 * 10
.
- Сначала вычисляется
2 * 4
и получается выражение8 * 5 * 10
. - Затем
8 * 5
. В итоге имеем40 * 10
. - В конце концов происходит последнее умножение, и получается результат
400
.
Задание
Реализуйте программу, которая вычисляет и выводит на экран значение выражения:
8 / 2 + 5 - -3 / 2
Не вычисляйте ничего самостоятельно, ваша программа должна производить все вычисления сама.
10. Приоритет
Python: Приоритет
Посмотрите внимательно на выражение 2 + 2 * 2
и посчитайте в уме ответ.
Правильный ответ: 6
.
Если у вас получилось 8
, то этот урок для вас. В школьной математике мы изучали понятие «приоритет операции». Приоритет определяет то, в какой последовательности должны выполняться операции. Например, умножение и деление имеют больший приоритет, чем сложение и вычитание, а приоритет возведения в степень выше всех остальных арифметических операций: 2 ** 3 * 2
вычислится в 16
.
Но нередко вычисления должны происходить в порядке, отличном от стандартного приоритета. В сложных ситуациях приоритет можно (и нужно) задавать круглыми скобками, точно так же, как в школе, например: (2 + 2) * 2
.
Скобки можно ставить вокруг любой операции. Они могут вкладываться друг в друга сколько угодно раз. Вот пара примеров:
print(3 ** (4 - 2)) # => 9
print(7 * 3 + (4 / 2) - (8 + (2 - 1))) # => 14
Главное при этом соблюдать парность, то есть закрывать скобки в правильном порядке. Это, кстати, часто становится причиной ошибок не только у новичков, но и у опытных программистов. Для удобства ставьте сразу открывающую и закрывающую скобку, а потом пишите внутреннюю часть. Редактор на нашем сайте (и большинство других редакторов кода) делают это автоматически: вы пишете (
, а редактор сразу добавляет )
. Это касается и других парных символов, например, кавычек. О них — в будущих уроках.
Иногда выражение сложно воспринимать визуально. Тогда можно расставить скобки, не повлияв на приоритет. Например, задание из прошлого урока можно сделать немного понятнее, если расставить скобки.
Было:
print(8 / 2 + 5 - -3 / 2) # => 10.5
Стало:
print(((8 / 2) + 5) - (-3 / 2)) # => 10.5
Запомните: код пишется для людей, потому что код будут читать люди, а машины будут только исполнять его. Для машин код — или корректный, или не корректный, для них нет «более» понятного или «менее» понятного кода.
Задание
Дано вычисление 70 * 3 + 4 / 8 + 2
.
Расставьте скобки так, чтобы оба сложения (3 + 4
) и (8 + 2
) высчитывались в первую очередь. Выведите на экран результат.
11. Линтер
Python: Линтер
Теперь, когда мы уже научились писать простые программы, можно немного поговорить о том, как их писать.
Код нужно оформлять определенным образом, чтобы он был достаточно понятным и простым в поддержке. Специальные наборы правил — стандарты — описывают различные аспекты написания кода. Конкретно в Python стандарт один — PEP8. Он отвечает практически на все вопросы, о том, как оформлять ту или иную часть кода.
В любом языке программирования существуют специальные инструменты — так называемые линтеры. Они проверяют код на соответствие стандартам. В Python их достаточно много, и наиболее популярный из них — flake8.
Взгляните на пример:
result = 1+ 3
Линтер будет «ругаться» на нарушение правила: E225 missing whitespace around operator. По стандарту, оператор +
всегда должен отделяться пробелами от операндов.
E225 — лишь одно из большого количества правил. Другие правила описывают отступы, названия создаваемых сущностей, скобки, математические операции, длину строчек и множество иных аспектов. Каждое отдельное правило кажется довольно мелким, не очень важным. Но вместе они составляют основу хорошего кода. Список всех правил flake8 доступен тут.
Сайт сейчас не будет проверять ваш код линтером, но в ваших будущих практиках на Хекслете (и в реальной разработке) линтер будет работать и сообщать вам о нарушениях.
Задание
Выведите на экран результат следующего вычисления: «разница между пятью в квадрате и произведением трёх и семи». Напишите код так, чтобы каждый оператор отделялся от операндов пробелами.
Строки
Текст в программировании называется «строками», и эта тема не так проста, как может показаться. Как вывести фразу, в которой есть и одинарные, и двойные кавычки? Что такое экранированная последовательность? Модуль посвящен разным аспектам написания текста – мы поэкспериментируем с выводом разных строк и поговорим о кодировке.
12. Кавычки
Python: Кавычки
'Hello'
'Goodbye'
'G'
' '
''
Какие из этих пяти вариантов — строки?
С первыми двумя все понятно, это точно строки, мы уже работали с подобными конструкциями и говорили, что строки — это наборы символов.
Любой одиночный символ в кавычках — это строка. Пустая строка ''
— это тоже строка. То есть строкой мы считаем всё, что находится внутри кавычек, даже если это пробел, один символ или вообще отсутствие символов.
Ранее в уроках мы записывали строки в одинарных кавычках, но это не единственный способ. Можно использовать и двойные:
print("Dracarys!")
Теперь представьте, что вы хотите напечатать строчку Dragon’s mother. Апостроф перед буквой s — это такой же символ, как одинарная кавычка. Попробуем:
print('Dragon's mother')
# SyntaxError: invalid syntax
Такая программа не будет работать. С точки зрения Python строчка началась с одинарной кавычки, а потом закончилась после буквы n. Дальше были символы s mother
без кавычек — значит, это не строка. А потом была одна открывающая строку кавычка, которая так и не закрылась: ')
. Этот код синтаксически некорректен (это видно даже по тому, как подсвечен код).
Здесь нам помогут именно двойные кавычки. Такой вариант программы отработает корректно:
print("Dragon's mother")
Теперь интерпретатор знает, что строка началась с двойной кавычки — значит и закончиться должна на двойной кавычке. А одинарная кавычка внутри стала частью строки.
Верно и обратное. Если внутри строки мы хотим использовать двойные кавычки, то саму строку надо делать в одинарных. Причем количество кавычек внутри самой строки не важно.
А что, если мы хотим создать такую строку:
Dragon's mother said "No"
В ней есть и одинарные и двойные кавычки. Как быть в этой ситуации? Нужно каким-то образом указать интерпретатору считать каждую кавычку частью строки, а не началом или концом строки.
Для этого используют символ экранирования: \
(обратный слэш). Если поставить \
перед кавычкой (одинарной или двойной), это скажет интерпретатору о том, что кавычку нужно рассматривать не как начало или конец строки, а как часть строки.
# Экранируется только ", так как в этой ситуации
# двойные кавычки имеют специальное значение
print("Dragon's mother said \"No\"")
# => Dragon's mother said "No"
Посмотрите внимательно: нам нужно было добавить \
для двойных кавычек, но не для одинарной (апостроф), потому что сама строка создана с двойными кавычками. Если бы строка создавалась с одинарными кавычками, то символ экранирования нужен был бы перед апострофом, но не перед двойными кавычками.
Задание
Напишите программу, которая выведет на экран:
"Khal Drogo's favorite word is "athjahakar""
Программа должна вывести на экран эту фразу в точности.
13. Экранированные последовательности
Python: Экранированные последовательности
Мы хотим показать диалог Матери Драконов со своим ребенком:
- Are you hungry?
- Aaaarrrgh!
Если вывести на экран строку с таким текстом:
print("- Are you hungry?- Aaaarrrgh!")
то получится так:
- Are you hungry?- Aaaarrrgh!
Не то, что мы хотели. Строки расположены друг за другом, а не одна ниже другой. Нам нужно как-то сказать интерпретатору «нажать на энтер» — сделать перевод строки после вопросительного знака. Это можно сделать, используя символ перевода строки: \n
.
print("- Are you hungry?\n- Aaaarrrgh!")
результат:
- Are you hungry?
- Aaaarrrgh!
\n
– это пример экранированной последовательности (escape sequence). Их еще называют управляющими конструкциями. Эти конструкции не являются видимой частью строки, их нельзя увидеть глазами в том же виде, в котором они были набраны.
Набирая текст в каком-нибудь Word, вы нажимаете на Enter в конце строчки. Редактор при этом ставит в конец строчки специальный невидимый символ, который называется LINE FEED (LF, перевод строчки). В некоторых редакторах можно даже включить отображение невидимых символов. Тогда текст будет выглядеть примерно так:
- Привет!¶
- О, привет!¶
- Как дела?
Устройство, которое выводит соответствующий текст, учитывает этот символ. Например, принтер при встрече с LF протаскивает бумагу вверх на одну строку, а текстовый редактор переносит весь последующий текст ниже, также на одну строку.
Хотя таких символов не один десяток, в программировании часто встречаются всего несколько. Кроме перевода строки, к таким символам относятся табуляция \t
(разрыв, получаемый при нажатии на кнопку Tab) и возврат каретки \r
(только в Windows). Распознать управляющую конструкцию в тексте проще всего по символу \
. Нам, программистам, часто нужно использовать, например, перевод строки \n
для правильного форматирования текста.
print("Gregor Clegane\nDunsen\nPolliver\nChiswyck")
На экран выведется:
Gregor Clegane
Dunsen
Polliver
Chiswyck
Обратите внимание на следующие моменты:
1. Не имеет значения, что стоит перед или после \n
: символ или пустая строка. Перевод будет обнаружен и выполнен в любом случае.
2. Помните, что строка может содержать лишь один символ или вообще ноль символов? А еще строка может содержать только \n
:
print('Gregor Clegane')
print("\n")
print('Dunsen')
Здесь мы выводим одну строку с именем, потом одну строку «перевод строки», а потом еще одну строку. Программа выведет на экран:
Gregor Clegane
Dunsen
3. Несмотря на то, что в исходном тексте программы последовательность типа \n
выглядит как два символа, с точки зрения интерпретатора это специальный один символ.
4. Если нам понадобится вывести \n
именно как текст (два отдельных печатных символа), то можно воспользоваться уже известным нам способом экранирования, добавив еще один \
в начале. То есть последовательность \\n
отобразится как символы \
и n
, идущие друг за другом.
print("Joffrey loves using \\n")
на экран выйдет:
Joffrey loves using \n
Небольшое, но важное замечание про Windows. В Windows для перевода строк по умолчанию используется \r\n
. Такая комбинация хорошо работает только в Windows, но создает проблемы при переносе в другие системы (например, когда в команде разработчиков есть пользователи как Windows, так и Linux). Дело в том, что последовательность \r\n
имеет разную трактовку в зависимости от выбранной кодировки (рассматривается позже). По этой причине в среде разработчиков принято всегда использовать \n
без \r
, так как LF всегда трактуется одинаково и отлично работает в любой системе. Не забудьте настроить ваш редактор на использование \n
.
Задание
Напишите программу, которая выводит на экран:
- Did Joffrey agree?
- He did. He also said "I love using \n".
При этом программа использует только один print
, но результат на экране должен выглядеть в точности как показано выше.
Советы
- Обязательно поэкспериментируйте с выводом разных строк на сайте https://repl.it/languages/python3
- История перевода строки
Определения
- Экранированная последовательность – специальная комбинация символов в тексте. Например,
— это перевод строки.
14. Конкатенация
Python: Конкатенация
В веб-разработке программы постоянно оперируют строками. Всё, что мы видим на сайтах, так или иначе представлено в виде текста. Этот текст чаще всего динамический, то есть полученный из разных частей, которые соединяются вместе. Операция соединения строк в программировании называется конкатенацией.
# Оператор такой же, как и при сложении чисел
# но здесь он имеет другой смысл (семантику)
print('Dragon' + 'stone');
# => 'Dragonstone'
Склеивание строк всегда происходит в том же порядке, в котором записаны операнды. Левый операнд становится левой частью строки, а правый — правой.
Вот еще несколько примеров:
print('Kings' + 'wood') # => Kingswood
# Обратный порядок слов
print('Kings' + 'road') # => Kingsroad
# Конкатенировать можно абсолютно любые строки
print("King's" + 'Landing') # => King'sLanding
Как видите, строки можно склеивать, даже если они записаны с разными кавычками.
В последнем примере название города получилось с ошибкой: King’s Landing нужно писать через пробел! Но в наших начальных строках не было пробелов, а пробелы в самом коде слева и справа от символа +
не имеют значения, потому что они не являются частью строк.
Попробуем решить эту проблему разными способами:
# Оба способа равнозначны
# Ставим пробел в левой части
print("King's " + 'Landing') # => King's Landing
# Ставим пробел в правой части
print("King's" + ' Landing') # => King's Landing
Пробел — такой же символ, как и другие. Чем больше пробелов, тем шире отступы:
print("King's " + ' Landing') # => King's Landing
print("King's " + ' Landing') # => King's Landing
Задание
Выведите на экран
Winter came for the House of Frey.
используя конкатенацию слов.
Определения
- Конкатенация – операция соединения двух строк. Например,
print("King's " + ' Landing')
15. Кодировка
Python: Кодировка
На самом глубоком уровне компьютер оперирует исключительно цифрами 0
и 1
. Это так называемый двоичный код, а единички и нули называются битами, от “binary digit” — «двоичная цифра».
Обычные, привычные нам числа в десятичной системе исчисления, закодированы с помощью двоичных чисел:
- 0 ← 0
- 1 ← 1
- 2 ← 10
- 3 ← 11
- 4 ← 100
- 5 ← 101
Но как быть с текстом? Компьютер на самом деле не знает ничего о буквах, знаках пунктуации и прочих текстовых символах. Все эти символы так же закодированы числами.
Можно взять английский алфавит и дать каждой букве число, начиная с единицы по порядку:
- a ← 1
- b ← 2
- c ← 3
- d ← 4
- …
- z ← 26
В этом заключается суть кодировок.
Во время своей работы, программы используют кодировки для преобразования чисел в символы и наоборот. Причём сама программа не имеет представления о смысле этих символов.
hello
→8
5
12
12
15
7
15
15
4
→good
Подобные таблицы, в которых сопоставляются буквы и числа, называются кодировками. Кроме букв алфавита, в таблицы кодировок входят знаки препинания и другие полезные символы. Вы наверняка сталкивались с кодировками, например, ASCII или UTF-8.
Разные кодировки содержат разное количество символов. Изначально небольших таблиц вроде ASCII было достаточно для большинства задач. Но в ней только латинские буквы, несколько простых символов вроде %
и ?
и специальные управляющие символы типа перевода строки.
С распространением компьютеров, разным странам понадобились свои, более широкие таблицы. В том числе для кириллических букв, восточных иероглифов, арабской вязи, дополнительных математических и типографских символов, а впоследствии даже для эмодзи-смайликов.
Сегодня в большинстве случаев используется один из вариантов юникода, включающий в себя знаки почти всех письменных языков мира.
Задание
В Python можно «запросить» и вывести на экран любой символ из кодировки ASCII. Например:
print(chr(63))
На экран выведется символ с номером 63 — вопросительный знак ?
. Таким способом можно выводить любой символ.
Найдите в интернете таблицу кодов ASCII. Можно использовать запросы типа “ascii codes table” или «коды ascii». Обычно в таких таблицах коды указаны сразу в нескольких системах счисления: десятичной, двоичной, восьмеричной и шестнадцатеричной. Нас интересует десятичный код (dec или decimal).
Используя пример выше и найденную таблицу, выведите на экран (каждый на своей собственной строке) ~
, ^
и %
.
(Конечно, можно «обмануть» тесты и просто сделать print('~')
и т.д., но так будет совсем неинтересно 🙂
Определения
- Кодировка – набор символов, закодированных с помощью чисел для представления текста в электронном виде.
Типы данных
Python – язык с сильной типизацией и неизменяемыми примитивными типами данных. Что произойдет, если мы попробуем умножить число на строку? Каким образом Python понимает, что за тип данных перед ним? Что такое преобразование типов? Ответы на эти вопросы вы найдете в текущем модуле.
16. Типы данных
Python: Типы данных
Бывают разные способы представлять данные в программах.
Есть строки — наборы символов в кавычках вроде "Hello, World!"
. Есть целые числа — например, 7
, -198
, 0
. Это две разные категории информации — два разных типа данных.
Операция умножения имеет смысл для категории «целые числа». Но не имеет смысла для категории «строки»: умножать слово «мама» на слово «блокнот» — бессмыслица.
Тип данных определяет, что можно делать с элементами конкретного множества информации.
Язык программирования распознает типы. Поэтому Python не позволит нам умножать строку на строку («умножать текст на текст»). Но позволит умножать целое число на другое целое число. Наличие типов и таких ограничений в языке защищает программы от случайных ошибок.
В отличие от строк, числа оборачивать в кавычки не нужно. Чтобы напечатать число 5, достаточно написать:
print(5)
Обратите внимание, что число 5
и строка '5'
— совершенно разные вещи, хотя вывод у print
для этих данных идентичный.
Целые числа (1
, 34
, -19
и т.д.) и рациональные числа (1.3
, 1.0
, -14.324
и т.д.) — это два отдельных типа данных. Такое разделение связано с особенностями устройства компьютеров. Есть и другие типы, с ними мы познакомимся позже.
Вот еще один пример, но уже с рациональным числом:
print(10.234)
Типы данных «строка», «целое число» и «рациональное число» — это примитивные типы, они встроены в сам язык Python. В язык встроены также и некоторые составные типы данных, но пока мы будем работать только с примитивными. Программисты также могут создавать собственные типы данных.
(По-английски строки в программировании называются “strings”, а строчки текстовых файлов называются “lines”. Например, в примере кода выше есть строчка (line), и нет никаких строк (strings). В русском иногда может быть путаница, поэтому во всех уроках мы будем говорить строка для обозначения типа данных «строка», и строчка для обозначения строчек кода (lines) в файлах).
Задание
Выведите на экран число -0.304
.
Советы
- Статья о дробных числах
Определения
- Тип данных – множество данных в коде (разновидность информации). Тип определяет, что можно делать с элементами конкретного множества. Например, целые числа, рациональные числа, строки — это разные типы данных.
- Примитивные типы данных – простые типы, встроенные в сам язык программирования.
- Строка (string) – тип данных, описывающий набор символов (иными словами — текст) например,
'text'
или"text"
17. Сильная типизация
Python: Сильная типизация
Нам известно про два разных типа данных: числа и строки. Мы, например, могли складывать числа, потому что операция сложения — это операция для типа «числа».
А что, если применить эту операцию не к двум числам, а к числу и строке?
print(1 + '7') # TypeError: unsupported operand type(s)...
Python не разрешит сложить число 1
и строку '7'
, потому что это значения разных типов. Нужно сначала либо сделать строку числом, либо число строкой (как это сделать, мы поговорим позже). Такое педантичное отношение к совместимости типов называется строгой типизацией. Python – язык со строгой типизацией.
Не все языки так делают. Скажем, PHP, это язык со слабой типизацией. Он знает о существовании разных типов (числа, строки и др.), но относится к их использованию не очень строго, пытаясь преобразовывать информацию, когда это кажется разумным.
Такое автоматическое неявное преобразование типов с одной стороны и правда кажется удобным. Но на практике это свойство языка создает множество ошибок и проблем, которые трудно найти. Код может иногда работать, а иногда не работать — в зависимости от того, «повезло» ли в конкретном случае с автоматическим преобразованием. Программист это заметит не сразу.
Задание
Выведите на экран результат выражения: 7 - (-8 - -2)
. Попробуйте сделать число 7 не числом, а строкой. Поэкспериментируйте с другими числами тоже.
18. Явное преобразование типов
Python: Явное преобразование типов
Преобразование типов — довольно частая операция при программировании на языке с сильной типизацией.
# Примеры немного искусственные.
# В реальности этот механизм становится полезен
# при использовании переменных
print(str(5))
print(int('345'))
Явное преобразование типа производят функции str
и int
. Функция str
приводит значение к строке, а функция int
— к числу.
Преобразование типов можно использовать внутри составных выражений:
print('Это ' + str(5))
Это 5
В более сложных ситуациях (при использовании функций, которые мы пройдём позже) встречаются множественные преобразования: str(5 + int('4'))
. Порядок вычисления этого выражения следующий:
int('4') # 4
5 + 4 # 9
str(9) # '9'
Задание
Выведите на экран строку 2 times
, полученную из числа 2 и строки times
, используя преобразования типов и конкатенацию.
Переменные
Информацию можно помещать в специальные «хранилища», а потом использовать сколько угодно раз. Эти хранилища называются переменными, и они помогают упростить код и сократить лишние повторения.
19. Что такое переменная
Python: Что такое переменная
Представьте себе задачу, нам нужно напечатать на экран фразу Father! два раза или даже пять раз. Эту задачу можно решить в лоб:
print('Father!')
print('Father!')
В простейшем случае так и стоит поступить, но если фраза Father! начнет использоваться чаще, да еще и в разных частях программы, то придется ее везде повторять. Проблемы с таким подходом начнутся тогда, когда понадобится изменить нашу фразу, а такое происходит довольно часто. Нам придется найти все места где использовалось фраза Father! и выполнить необходимую замену. А можно поступить по другому. Вместо копирования нашего выражения, достаточно создать переменную с этой фразой.
# greeting - переводится как приветствие
greeting = 'Father!'
print(greeting)
print(greeting)
В строчке greeting = 'Father!'
– происходит присваивание значения 'Father!'
переменной с именем greeting
. Переменная указывает на данные, которые были в неё записаны. Благодаря этому, данные можно использовать многократно без необходимости их постоянно дублировать.
Когда переменная создана, можно начать её использовать. Она подставляется в те места, где раньше стояла наша фраза. Во время выполнения кода, интерпретатор (то, что выполняет код на Python), доходит до строчки print(greeting)
и подставляет вместо переменной её содержимое, а затем выполняет код. В результате вывод нашей программы будет таким:
Father! Father!
Для имени переменной используется любой набор допустимых символов, к которым относятся буквы английского алфавита, цифры и знак _
. При этом цифру нельзя ставить в начале. Имена переменных регистрозависимы, то есть имя hello
и имя heLLo
– это два разных имени, а значит и две переменные. Регистр в Python имеет важное значение, никогда не забывайте про него.
Количество создаваемых переменных никак не ограничено, большие программы содержат десятки и сотни тысяч имен переменных:
greeting1 = 'Father!'
print(greeting1)
print(greeting1)
greeting2 = 'Mother!'
print(greeting2)
print(greeting2)
Для удобства анализа программы, переменные принято создавать как можно ближе к тому месту где они используются.
Задание
Создайте переменную с именем motto
и содержимым What Is Dead May Never Die!
. Распечайте содержимое переменной.
Советы
- Именование в программировании
Определения
- Переменная – способ сохранить информацию и дать ей имя для последующего использования в коде.
20. Изменение переменной
Python: Изменение переменной
Само слово “переменная”, говорит о том, что ее можно менять. И действительно, с течением времени внутри программы, значения переменных могут изменяться.
# greeting - переводится как приветствие
greeting = 'Father!'
print(greeting)
print(greeting)
greeting = 'Mother!'
print(greeting)
print(greeting)
Имя осталось тем же, но внутри другие данные. Следует заметить, что переменные в Python не требуют специального объявления. Вместо этого переменная объявляется при первом её использовании в программе.
Переменные – мощная и в тоже время опасная вещь. Никогда нельзя быть точно уверенным, что внутри неё записано, не проанализировав код, который находится перед переменной. Именно этим занимаются разработчики во время отладки, когда пытаются разобраться, почему программа не работает или работает не так, как задумано.
Задание
В упражнении определена переменная, внутри которой строка. Переопределите значение этой переменной и присвойте ей строку, в которой расположите символы первоначальной строки в обратном порядке.
Обратите внимание: в данном задании вам предстоит писать код между строчками с комментариями BEGIN
и END
(об этом говорилось ранее, но это первый раз, когда вы встречаетесь с таким форматом).
Определения
- Переменная – способ сохранить информацию и дать ей имя для последующего использования в коде.
21. Ошибки при работе с переменными
Python: Ошибки при работе с переменными
Порядок следования инструкций в коде с переменными играет огромное значение. Переменная должна быть определена до того, как будет использована. Ниже пример ошибки, которую очень часто допускают новички:
print(greeting)
greeting = 'Father!'
Запуск программы, выше завершается с ошибкой NameError: name 'greeting' is not defined
– это ошибка обращения, она означает, что в коде используется имя (говорят идентификатор), который не определен. Причём в самой ошибке об этом говорят прямо: 'greeting' is not defined
. Кроме неправильного порядка определения, в Python встречаются банальные опечатки, причём как при использовании переменной, так и при её объявлении.
Количество подобных ошибок уменьшается за счет использования правильно настроенного редактора. Такой редактор подсвечивает имена, которые используются без объявления и предупреждает о возможных проблемах.
Задание
Найдите в программе необъявленную переменную и объявите ее, присвоив ей значение ‘Dragon’.
Определения
- Переменная – способ сохранить информацию и дать ей имя для последующего использования в коде.
22. Выбор имени переменной
Python: Выбор имени переменной
Представим себе, что программа из прошлого урока выглядит так:
x = 'Father!'
print(x)
Она по-прежнему работает, но в ней изменилось имя переменной на x
. Компьютеру без разницы, как мы называем переменные, это бездушная машина, но вот программистам — нет. Мы гораздо чаще читаем код, чем пишем. Причём не свой, а написанный другими людьми. От качества и понятности имён переменных зависит половина успеха в анализе кода.
Лучше посидеть и придумать название, которое описывает суть, смысл переменной, чем назвать её как попало, а в будущем переделывать. Постарайтесь давать им такие имена, чтобы они были максимально понятны без контекста, без изучения окружающего кода.
Существует общепринятое правило: не используйте транслит для имён, только английский язык. Если вы испытываете сложности с английским, то пользуйтесь переводчиком. Со временем, копаясь в чужом коде, вы сформируете правильные понятия для именования.
Среди разработчиков есть шутка: «самое сложное в программировании — названия переменных и инвалидация кеша». Придумывать названия и правда сложно. Как бы вы назвали переменную, в которой хранится количество неоплаченных заказов от клиентов, имеющих задолженность в предыдущем квартале?
Самопроверка. Придумайте название для переменной, в которой будет храниться «количество братьев и сестёр короля». Запишите его в блокноте или отправьте себе на почту. Не указывайте там ничего, кроме названия переменной. А через несколько уроков мы вернёмся к этой теме 😉
Задание
Создайте переменную, описывающую количество моих братьев, и присвойте ей значение 2. Распечатайте содержимое переменной. Затем сравните свое имя с именем, которое используется в учительском решении.
Советы
- Именование в программировании
- Ошибки в именовании переменных
23. Выражения в определениях
Python: Выражения в определениях
Переменные полезны не только для хранения и переиспользования информации, но и для упрощения сложных вычислений. Давайте рассмотрим пример: нужно перевести евро в рубли через доллары. Подобные конвертации через промежуточную валюту часто делают банки при покупках за рубежом.
Для начала переведем 50 евро в доллары. Допустим, что один евро — 1.25 долларов:
dollars_count = 50 * 1.25
print(dollars_count) # => 62.5
В предыдущем уроке мы записывали в переменную конкретное значение. А здесь dollars_count = 50 * 1.25
справа от знака равно находится выражение. Интерпретатор вычислит результат — 62.5
— и запишет его в переменную. С точки зрения интерпретатора не важно, что перед ним: 62.5
или 50 * 1.25
, для него оба варианта — выражения, которые надо вычислить. И они вычисляются в одно и тоже значение — 62.5
.
Любая строка — выражение. Конкатенация строк — тоже выражение. Когда интерпретатор видит выражение, он обрабатывает его и генерирует результат — значение выражения. Вот несколько примеров выражений, а в комментариях справа от каждого выражения — итоговое значение:
62.5 # 62.5
50 * 1.25 # 62.5
120 / 10 * 2 # 24
int('100') # 100
'hello' # 'hello'
'Good' + 'will' # 'Goodwill'
Правила построения кода (грамматика языка) таковы, что в тех местах, где ожидается выражение, можно поставить любое вычисление (не только математическое, но и, например, строковое — как конкатенация), и программа останется работоспособной. По этой причине невозможно описать и показать все случаи использования всех операций.
Программы состоят из множества комбинаций выражений, и понимание этой концепции — один из ключевых шагов на вашем пути.
Основываясь на сказанном выше, подумайте, сработает ли такой код?
who = "dragon's " + 'mother'
print(who)
Запустите его на repl.it и поэкспериментируйте.
Вернемся к нашей валютной программе. Запишем стоимость доллара в рублях, как отдельную переменную. Вычислим цену 50 евро в долларах, умножив их на 1.25
. Допустим, что 1 доллар — 60 рублей:
rubles_per_dollar = 60
dollars_count = 50 * 1.25 # 62.5
rubles_count = dollars_count * rubles_per_dollar # 3750.0
print(rubles_count)
А теперь давайте добавим к выводу текст с помощью конкатенации:
rubles_per_dollar = 60
dollars_count = 50 * 1.25 # 62.5
rubles_count = dollars_count * rubles_per_dollar # 3750.0
print('The price is ' + str(rubles_count) + ' rubles')
The price is 3750.0 rubles
Любая переменная может быть частью любого выражения. В момент вычисления вместо имени переменной подставляется её значение.
Интерпретатор вычисляет значение dollars_count
до того, как эта переменная начнет использоваться в других выражениях. Когда подходит момент использования переменной, Python «знает» значение, потому что уже вычислил его.
Задание
Напишите программу, которая берет исходное количество евро, записанное в переменную euros_count
, переводит евро в доллары и выводит на экран. Затем полученное значение переводит в рубли и выводит на новой строчке.
Пример вывода для 100 евро:
125.0
7500.0
Считаем, что:
– 1 евро = 1.25 долларов
– 1 доллар = 60 рублей
24. Переменные и конкатенация
Python: Переменные и конкатенация
Для закрепления предыдущей темы попробуем использовать переменные с конкатенацией. Синтаксически ничего не меняется: мы умеем конкатенировать (склеивать) две строки:
what = "Kings" + "road"
print(what) # => "Kingsroad"
… а значит сумеем конкатенировать строку и одну переменную, в которой записана строка:
first = "Kings"
what = first + "road"
print(what) # => "Kingsroad"
… и даже конкатенировать две переменные, в которых записаны строки:
first = "Kings"
last = 'road'
what = first + last
print(what) # => "Kingsroad"
Задание
Сайты постоянно посылают письма своим пользователям. Типичная задача — сделать автоматическую отправку персонального письма, где в заголовке будет имя пользователя. Если где-то в базе сайта хранится имя человека в виде строки, то задача генерации заголовка сводится к конкатенации: например, нужно склеить строку Здравствуйте
со строкой, где записано имя.
Напишите программу, которая будет генерировать заголовок и тело письма, используя уже готовые переменные, и выводить получившиеся строки на экран.
Для заголовка используйте переменные first_name
и greeting
, запятую и восклицательный знак. Выведите это на экран в правильном порядке.
Для тела письма используйте переменные info
и intro
, при этом второе предложение должно быть на новой строке.
Результат на экране будет выглядеть так:
Hello, Joffrey! Here is important information about your account security. We couldn't verify you mother's maiden name.
Выполните задание, используя только два print()
.
Советы
- Подумайте, с какой строкой и в каком порядке нужно склеивать переменные, чтобы получить такой двустрочный вывод тела письма.
- Помните, что можно создать строку, которая содержит только управляющую последовательность
\n
.
25. Именование переменных
Python: Именование переменных
greeting
— пример простого имени, но не все имена так просты. Довольно часто они составные, то есть включают в себя несколько слов. Например, «имя пользователя». В разных языках применяются разные стили кодирования, и имя переменной будет отличаться.
В именовании переменных можно выделить три основных подхода, которые иногда комбинируют друг с другом. Все эти подходы проявляют себя, когда имя переменной состоит из нескольких слов:
- kebab-case — составные части переменной разделяются дефисом. Например:
my-super-var
. - snake_case — для разделения используется подчеркивание. Например:
my_super_var
. - CamelCase — каждое слово в переменной пишется с заглавной буквы. Например:
MySuperVar
.
Переменные в Python именуются в стиле snake_case
: слова записываются строчными буквами и разделяются символом подчёркивания (_
).
В каждом языке свои правила. Большинство решений о том, как и что должно делаться в мире Python, закреплено в так называемых PEP — Python Enhancement Proposals, Предложениях по Улучшению языка Python. Один из наиболее важных PEP — PEP8. Этот документ содержит все правила, которых нужно придерживаться при написании Python-кода, в частности — как называть переменные. Советуем с самого начала завести привычку заглядывать в стандарт PEP8 и писать код в соответствии с ним.
К счастью, сегодня не нужно помнить все правила из стандарта, потому что существуют специальные программы, которые проверяют код автоматически и сообщают о нарушениях. Такие программы называются линтерами, и вы начнёте их использовать немного позже, когда у вас появится чуть больше опыта.
На Хекслете мы начинаем работать с линтерами почти с самого начала.
Задание
Создайте две переменные с именами «первое число» и «второе число» на английском языке используя snake_case. Запишите в первую переменную число 20
, во вторую — -100
. Выведите на экран произведение чисел, записанных в получившиеся переменные.
Код будет работать с любыми названиями, а наша система всегда проверяет только результат на экране, поэтому выполнение этого задания — под вашу ответственность.
Определения
- Стандарт кодирования – набор синтаксических и стилистических правил написания кода.
26. Магические числа
Python: Магические числа
Вспомним один из прошлых уроков:
euros_count = 1000
dollars_count = euros_count * 1.25 # 1250.0
rubles_count = dollars_count * 60 # 75000.0
print(rubles_count)
С точки зрения профессиональной разработки, такой код «пахнет». Так описывают код, который не соответствует так называемым лучшим практикам (best practices). И причина здесь вот в чем: уже сейчас, глядя на числа 60
и 1.25
, вы скорее всего задаетесь вопросом: «что это за числа?». А представьте, что будет через месяц! А как его поймет новый программист, не видевший код ранее? В нашем примере контекст восстанавливается благодаря грамотному именованию, но в реальной жизни код значительно сложнее, и поэтому догадаться до смысла чисел зачастую невозможно.
Этот «запах» называют магические числа (magic numbers). Числа, происхождение которых невозможно понять без глубокого знания происходящего внутри данного участка кода.
Выход из ситуации прост: достаточно создать переменные с правильными именами, как все встанет на свои места.
dollars_per_euro = 1.25
roubles_per_dollar = 60
euros_count = 1000
dollars_count = euros_count * dollars_per_euro # 1250.0
rubles_count = dollars_count * roubles_per_dollar # 75000.0
print(rubles_count)
Обратите внимание на следующие детали:
- Именование snake_case.
- Две новые переменные отделены от последующих вычислений пустой строчкой. Эти переменные имеют смысл и без вычислений, поэтому такое отделение уместно, оно повышает читаемость.
- Получился хорошо именованный и структурированный код, но он длиннее прошлой версии. Так часто бывает, и это нормально, потому что код должен быть читабельным.
Задание
Вы столкнулись с таким кодом, который выводит на экран общее количество комнат во владении нынешнего короля:
king = 'King Balon the 6th'
print(king + ' has ' + str(6 * 17) + ' rooms.')
Как видите, это магические числа: непонятно, что такое 6 и что такое 17. Можно догадаться, если знать историю королевской семьи: каждый новый король получает в наследство все замки от предков и строит новый замок — точную копию родительского.
Эта странная династия просто плодит одинаковые замки…
Избавьтесь от магических чисел, создав новые переменные, а затем выведите текст на экран.
Получится так:
King Balon the 6th has 102 rooms.
Названия переменных должны передавать смысл чисел, но должны при этом оставаться достаточно короткими и ёмкими для комфортного чтения.
Помните: код будет работать с любыми названиями, а наша система всегда проверяет только результат на экране, поэтому выполнение этого задания — под вашу ответственность.
27. Константы
Python: Константы
Некоторые данные, такие как математические постоянные, никогда не меняются. Возьмем число π. Приближенно оно всегда равно 3.14 и не может измениться.
Для обращения к подобным данным в Python принято использовать константы.
PI = 3.14
print(PI) # => 3.14
Создание константы не отличается от создания переменной. Однако константы принято именовать буквами в верхнем регистре с _
в качестве разделителя. Константа, как и переменная может использоваться в любом выражении.
Задание
Создайте константу DRAGONS_BORN_COUNT
и запишите в неё число 3 — это количество драконов, родившихся у Дайенерис.
Определения
- Константа – способ сохранить информацию и дать ей имя для последующего использования в коде; константы нельзя изменять, в отличие от переменных.
28. Интерполяция
Python: Интерполяция
В уроке про конкатенацию перед нами стояла задача создать заголовок письма из двух переменных и знаков препинания. Вы скорее всего решили задачу так:
first_name = 'Joffrey'
greeting = 'Hello'
print(greeting + ", " + first_name + "!")
# => 'Hello, Joffrey!'
Это довольно простой случай, но — наверное, вы согласитесь — выглядит это немного безобразно. Нужно следить за несколькими кавычками и пробелами, и без вглядывания не понять, где что начинается и кончается.
Есть другой, более удобный и изящный способ решения той же задачи — интерполяция. Вот, как это выглядит:
first_name = 'Joffrey'
greeting = 'Hello'
template = "{}, {}!"
print(template.format(greeting, first_name))
# => 'Hello, Joffrey!'
Мы просто создали специальную строку (template
) — так называемый “шаблон” — и разместили в ней несколько “пробелов” — пар фигурных скобок ({}
). Затем с помощью применения операции .format()
мы восполнили пробелы значениями переменных – по одной переменной на каждый “пробел” ({}
). В одном шаблоне можно оставлять несколько “пробелов”, но важно затем их заполнить все за один раз — подставить сразу нужное количество переменных.
Почти во всех языках интерполяция предпочтительнее конкатенации для объединения строк. Строка при этом получается склеенная, и внутри неё хорошо просматриваются пробелы и другие символы.
Задание
Выведите на экран строку Do you want to eat, <name>?
. Где вместо <name>
должна использоваться переменная stark
. Вывод должен получиться таким:
Do you want to eat, Arya?
Определения
- Интерполяция – способ получения сложной строки из нескольких простых с использованием специальных шаблонов.
29. Извлечение символов из строки
Python: Извлечение символов из строки
Иногда нужно получить один символ из строки. Например, если сайт знает имя и фамилию пользователя, и в какой-то момент требуется вывести эту информацию в формате A. Ivanov, то нужно взять первый символ из имени.
first_name = 'Alexander'
print(first_name[0]) # => A
Квадратные скобки с цифрой, это операция извлечения элемента по индексу — позицией символа внутри строки. Индексы начинаются с 0 почти во всех языках программирования — поэтому, чтобы получить первый символ, нужно указать индекс 0
. Индекс последнего элемента равен длине строки минус единица. Обращение к индексу за пределами строки приведёт к ошибке:
# Длина строки 9, поэтому последний индекс — это 8
first_name = 'Alexander'
print(first_name[8]) # => r
print(first_name[9])
IndexError: string index out of range
# Вопрос на самопроверку. Что выведет этот код?
magic = '\nyou'
print(magic[1]) # => ?
Допустимо использовать отрицательные индексы. В этом случае идёт обращение к символам, начиная с конца строки. -1
— индекс последнего символа, -2
предпоследнего и так далее. В отличие от прямой индексации, в обратной отсчёт идет от -1
:
first_name = 'Alexander'
print(first_name[-1]) # => r
Индексом может быть не только конкретное число, но и значение переменной. Вот пример, который приведёт к тому же результату — выводу на экран символа A, но индекс внутри квадратных скобок записан не числом, а переменной:
first_name = 'Alexander'
index = 0
print(first_name[index]) # => A
Задание
Вам даны три переменные с фамилиями разных людей. Составьте и выведите на экран слово из символов в таком порядке:
- третий символ из первой строки;
- второй символ из второй строки;
- четвертый символ из третьей строки;
- пятый символ из второй строки;
- третий символ из второй строки.
Попробуйте использовать интерполяцию: внутри фигурных скобок можно помещать не только целые переменные, но и отдельные символы с помощью квадратных скобок.
30. Multi-line строки
Python: Multi-line строки
Третий способ определения строк — это использование многострочных (multi-line) литералов. Для того, чтобы описать такую “многострочную строку”, нужно заключить её в тройные кавычки – """
или '''
. Между парой таких тройных кавычек можно переносить текст со строки на строку и все переносы строк сохранятся.
text = '''Пример текста,
состоящего из нескольких строк.
Здесь не нужно экранировать одинарные кавычки ' и двойные "
'''
print(text)
Пример текста, состоящего из нескольких строк. Здесь не нужно экранировать одинарные кавычки ' и двойные "
Обратите внимание на то, что в конце текста есть пустая строка. Эта строка появилась в тексте потому, что мы поставили закрывающие кавычки '''
на новой строке. Если не переносить закрывающие кавычки на новую строку, эта пустая строка в тексте не появится.
Задание
Запишите в переменную text
текст, который приведен ниже. Используйте тройные кавычки.
Lannister, Targaryen, Baratheon, Stark, Tyrell...
they're all just spokes on a wheel.
This one's on top, then that one's on top, and on and on it spins,
crushing those on the ground.
Вызов функций
Для выражения любой произвольной операции в программировании существует понятие «функция». Функции — кирпичики, из которых программисты строят системы. В этом модуле мы научимся пользоваться уже созданными функциями. Посмотрим на сигнатуру функции в документации и разберемся, как её использовать. Познакомимся со стандартными библиотеками, которые хранят тысячи функций. Все функции невозможно выучить, но каждый программист должен знать, где искать документацию по ним.
31. Функции и их вызов
Python: Функции и их вызов
Как вы заметили, для каждой операции требуется свой собственный символ (оператор): для сложения чисел — +
, для конкатенации строк — +
, для деления чисел — /
, и так далее. Такой подход оправдан только для самых частых операций, ведь в реальной жизни есть тысячи разных задач. К тому же, даже если бы язык программирования включал в себя тысячу разных операторов, в какой-то момент программисту понадобился бы новый.
Любой язык программирования позволяет расширять набор операций. Такие новые операции называются «функциями». Давайте сначала научимся пользоваться уже созданными функциями, а уже потом будем создавать новые функции.
В Python есть функция для возведения числа в какую-нибудь степень. Вызовем её:
result = pow(2, 3) # 8
Мы создали переменную result
и указали интерпретатору записать в неё результат, который выдаст функция pow
. Запись pow(2, 3)
означает, что вызывается функция с именем pow
и с аргументами (или с параметрами) 2 и 3. Вызванная функция вернула значение.
Аргументы — это информация, которую функция получает при вызове. Именно на основе этой информации функция, как правило, вычисляет что-то и выдает результат.
Функция pow
возводит первое число в степень второго числа. В нашем примере мы возводили 2 в степень 3, поэтому в result
в итоге оказалось значение 8.
Вызов функции всегда обозначается скобками ()
, идущими сразу за именем функции. В скобках может быть любое количество аргументов, а иногда — вообще ни одного. Количество зависит от выполняемой операции. Например, для нахождения модуля числа используется функция с именем abs
:
result = abs(-5) # 5
result = abs(8) # 8
Как видно, эта функция принимает один аргумент (часто говорят «параметр») и возвращает его модуль. А вот пример функции, которая не принимает аргументов:
result = str() # ''
Функция str
возвращает пустую строку, если вызвана без аргумента.
Но математические операции — капля в море по сравнению со всем многообразием функций. Мощь в том, что вы можете создавать собственные функции — мы займёмся этим в следующем модуле. Любое действие, которое вы себе можете вообразить, в программировании представлено функциями.
Задание
В 7 королевствах жил один человек, который имел доступ к компьютерам и умел программировать — Сэм Тарли. Он занимался картографией, поэтому он написал для себя функцию calculate_distance
, высчитывающую расстояние (в лигах) между городами. Функция принимает два строковых параметра — два названия городов, и возвращает число — расстояние между ними.
Вот пример использования, где на экран выводится расстояние между Lannisport и Bayasabhad:
from hexlet.code_basics import calculate_distance
distance = calculate_distance('Lannisport', 'Bayasabhad')
print(distance)
Первая строчка — это специальный код, подключающий функцию calculate_distance
в вашу программу. Благодаря ей вы можете запускать функцию, но не видите её содержимое. Это обычное дело в программировании: вы знаете, что делает функция и как ей пользоваться, но не знаете как именно она работает внутри.
Воспользуйтесь функцией calculate_distance
и выведите на экран расстояние между городами Qarth и Vaes Dothrak. Не копируйте пример, а создайте переменную с другим именем и напишите код с нуля самостоятельно.
Определения
- Функция – операция, способная принимать данные и возвращать результат; Имя функции, как и имя переменной, может быть любым, однако считается хорошим тоном называть функцию операцией, которую она выполняет; Если имя функции
foo
, то вызывается она так:foo()
. - Аргумент – информация, которую функция получает при вызове. Например,
foo(42)
— передача аргумента42
функцииfoo
.
32. Сигнатура функции
Python: Сигнатура функции
Функция pow()
, возводящая число в какую-нибудь степень, принимает два параметра: какое число возводить и в какую степень возводить. Если вызывать pow()
без параметров, то Python выдаст следующее: “TypeError: pow expected at least 2 arguments, got 0”. Интерпретатор сообщает, что функция ожидает 2 параметра, а вы вызвали её без параметров.
Тот, кто создавал функцию pow()
, сделал её функцией с двумя обязательными параметрами, поэтому pow()
невозможно вызвать с другим количеством параметров.
Более того, параметрами pow()
могут быть только числа. Попытка передать в неё, например, пару строк приведет к следующей ошибке: “TypeError: unsupported operand type(s) for ** or pow(): ‘str’ and ‘str’”. Результат вызова функции — тоже всегда число.
Другая функция может иметь другое число параметров и другие типы параметров. Например, может существовать функция, которая принимает три параметра: число, строку и еще одно число.
Откуда мы знаем, сколько каких параметров нужно функции pow()
и какого типа будет «возврат»? Мы заглянули в сигнатуру этой функции. Сигнатура определяет входные параметры и их типы, а также выходной параметр и его тип. Про функцию pow()
можно почитать в официальной документации Python или в неофициальной документации на русском языке. Обычно документация для функции имеет вид:
pow(x, y[, z])
Возвращает x в степени y; если z присутствует, возвращает x в степени y, по модулю z.
Первая строка здесь, это сигнатура функции. Следом идёт пояснение, для чего функция нужна. Документация позволяет понять сколько аргументов у функции и какого они типа, возвращает ли что-то функция и если да, то какой тип будет иметь возвращаемое значение.
Задание
Теперь ваша очередь посмотреть на сигнатуру функции в документации и разобраться, как её использовать. Можете читать документацию на русском языке, но программист обязан уметь читать документацию на английском. Используйте словари или переводчики при необходимости. Лучше сразу привыкать и подтягивать навыки чтения на английском, иначе будут сложности в будущем.
В Python есть функция hex()
. Изучите её сигнатуру на странице документации.
Напишите программу, которая использует функцию hex()
с переменной number
и выводит результат на экран.
Определения
- Сигнатура функции – формальное описание типов аргументов и типа возвращаемого значения функции.
33. Стандартная библиотека
Python: Стандартная библиотека
Python, как и любой другой язык, поставляется с набором полезных функций. Все вместе они составляют так называемую стандартную библиотеку. В неё обычно входят тысячи функций, которые невозможно выучить — этого и не нужно делать. Подразумевается, что любой программист знает, где искать документацию по ним и примерно представляет себе, чего он хочет достичь. А дальше — дело техники. Программировать без интернета крайне сложно.
Для новичков эта информация часто выглядит так: «Сходи туда, не знаю куда, принеси то, не знаю что». То есть непонятно как узнавать про эти функции, когда ты ничего не знаешь вообще. Как ни странно, не существует способа раз и навсегда познать всё, что нужно познать. Любой разработчик в процессе своего профессионального взросления знакомится со всё более интересными функциями, решающими его задачи более элегантно, и таким образом пополняет свой арсенал.
Вот некоторые советы, как узнавать о новых функциях:
- Всегда чётко отслеживайте, с чем вы сейчас работаете (какой тип данных). Почти всегда вы найдете необходимую функцию в соответствующем разделе документации, например, для работы со строками нужно изучать строковые функции.
- Периодически открывайте раздел со стандартными функциями по изучаемой тематике и просто пробегайтесь по ним, изучая сигнатуры и способы использования.
- Чаще читайте чужой код, особенно код библиотек которые вы используете. Он весь доступен на GitHub.
Задание
Функция type()
позволяет определить тип передаваемого аргумента. Название типа возвращается в виде строки. Например, вызов type(10)
вернёт строку <class 'int'>
(int, это сокращение от integer — целое число).
Выведите на экран тип значения переменной motto
.
Советы
- Справочник функций Python
Определения
- Стандартная библиотека – набор полезных функций, входящий в комплект поставки языка программирования.
34. Аргументы по умолчанию
Python: Аргументы по умолчанию
Рассмотрим функцию round()
. Она округляет переданное число:
result = round(10.25, 0) # 10
Мы передали в неё два аргумента: число и точность округления. 0
означает, что округление будет до целого значения.
Чаще всего нужно округлять именно до целого числа (а не до десятых, например), поэтому создатели функции round
сделали второй аргумент необязательным и задали ему внутри функции значение по умолчанию 0
. Значит, можно не указывать второй аргумент, а результат будет тем же:
result = round(10.25) # 10
А если нужна другая точность, то можно передать аргумент:
# округление до одного знака после запятой
result = round(10.25, 1) # 10.2
Если функция в Python принимает необязательные аргументы, то они всегда стоят после обязательных. Их количество может быть любым (это зависит от самой функции), но они всегда идут рядом и в конце списка аргументов.
Задание
Округлите число, записанное в переменную number
, до двух знаков после запятой и выведите результат на экран.
Определения
- Аргумент по умолчанию – необязательный аргумент функции.
35. Вызов функции — выражение
Python: Вызов функции — выражение
Посмотрите на код одного из предыдущих уроков:
distance = calculate_distance('Lannisport', 'Bayasabhad')
Что можно сказать о нём? Какие выводы сделать? Попробуйте закончить эти утверждения самостоятельно:
distance
— это __________- функция
calculate_distance()
вызывается с ___________'Lannisport'
и'Bayasabhad'
- функция
calculate_distance()
возвращает значение, тип которого — это _____. - после выполнения кода в переменной
distance
окажется ________, возвращенное вызовом функцииcalculate_distance()
Чтобы понять этот урок, нужно вспомнить понятие «выражение» из модуля про переменные. Напомним, что выражение — это код, который при выполнении программы превращается в значение.
Какие из этих фрагментов кода являются выражениями?
42
10 * 45
"Kings" + "road"
calculate_distance('Lannisport', 'Bayasabhad')
Числа и математические операции — наверное, самые простые варианты. Выражение 42
превратится в значение 42
, выражение 10 * 45
— в значение 450
.
Конкатенация строк — тоже выражение, которое превратится в соответствующее значение (новую строку).
Но вот четвёртый вариант! Это тоже выражение! Мощность и гибкость языка программирования во многом возможна благодаря тому, что вызов функции — это выражение.
Вернемся к первому примеру:
distance = calculate_distance('Lannisport', 'Bayasabhad')
В переменную distance
записывается результат вычисления выражения. В отличие от операций (например, 10 + 12
), где явно видно, какое вычисление производится, в функциях само вычисление скрыто от нас, и мы видим только результат. Поэтому говорят, что функция «возвращает» значение. Можно применить эту терминологию и к обычным операциям. Например, сказать, что конкатенация двух строк возвращает новую строку.
Что является выражением, а что нет? Сейчас может казаться, что это одна из скучных деталей из учебника по программированию. Но это действительно важный вопрос. Всё, что работает как выражение, может быть использовано в других выражениях, а также во всех местах, где на вход ожидаются выражения. Распознавать выражения в коде — важный навык, необходимый программисту каждый день.
Допустим, у нас есть функция number_of_knights()
, которая принимает название замка в королевстве и возвращает количество рыцарей в этом замке. Зная, что вызов функции — выражение, можно допустить, что такой код будет работать:
result = 4 + number_of_knights("Winterfell")
Потому что это сложение двух выражений: выражения 4
и выражения number_of_knights("Winterfell")
. В итоге получится 4 + какое-то число.
Значит, и такой код будет работать:
result = number_of_knights("Winterfell") + number_of_knights("Oldtown")
Здесь два разных вызова функций, но каждый вызов — выражение, поэтому в итоге получится сложение двух значений — двух чисел (количества рыцарей замка Winterfell и количества рыцарей замка Oldtown).
Задание
Арья собирается в путешествие из Винтерфела в Орлиное гнездо, чтобы навестить Лизу Аррен, но по пути ей нужно заехать к Фреям для совершения акта возмездия. Ей нужно рассчитать общую длину маршрута.
К сожалению, функция calculate_distance()
может вычислять расстояние только между двумя точками. Поэтому придется сначала узнать расстояние от Винтерфелла до замка Фреев, а потом расстояние до Орлиного гнезда.
Названия замков на английском языке:
- Винтерфелл —
Winterfell
. - Близнецы (Замок Фреев) —
The Twins
. - Орлиное гнездо —
The Eyrie
.
Выведите на экран полную длину маршрута Арьи. Напомним, что функция calculate_distance()
принимает два аргумента и возвращает число.
36. Выражения как аргументы
Python: Выражения как аргументы
Рассмотрим код из предыдущего урока:
result = round(10.25) # 10
Функция round()
вызывается с аргументом 10.25
.
Мы выяснили, что выражения превращаются в значения. То есть с точки зрения Python, значения и выражения — это что-то схожее. Поэтому любые значения в программе технически можно заменить выражениями.
При вызове функции можно передать в неё аргументом выражение:
result = round(8 + 2.25) # 10
Результат будет таким же, как в первом примере, потому что выражение 8 + 2.25
превратится в значение 10.25
, и с таким аргументом произойдет вызов round()
.
Более того, можно использовать переменные вперемешку со значениями и другими выражениями:
number = 1.25
result = round(number + 7 + 2) # 10
Естественно, это работает не только с числами, а с любыми значениями и выражениями. Например, со строками.
print('D' + 'ragon'); # => 'Dragon'
Напомним, что в составе Python есть функция hex()
, которая принимает число и возвращает строку с шестнадцатиричным представлением этого числа. Вот простой вызов:
result = hex(255) # '0xff'
А вот вызов, где в качестве единственного аргумента идёт целое выражение:
result = hex(200 + 55) # '0xff'
Результат будет тем же: в result
запишется строка '0xff'
, потому что выражение 200 + 55
превратится в значение 255
, и с таким аргументом произойдет вызов hex()
.
Давайте подытожим. Взгляните на несколько примеров из текущего урока:
# простые вызовы
result = round(10.25) # 10
result = hex(255) # '0xff'
# выражения в аргументах
result = round(8 + 2.25) # 10
result = hex(200 + 55) # '0xff'
print('D' + 'ragon'); # => 'Dragon'
# выражения с переменными в аргументах
number = 1.25
result = round(number + 7 + 2) # 10
digit = 5
result = hex(digit + 50 + 200) # '0xff'
Заметьте схожесть: во всех вызовах в функции передается какая-то информация, но иногда это простое, «готовое» значение (10.25
, 255
), а иногда составное выражение — «неготовое» значение (8 + 2.25
, number + 7 + 2
и т.д.). При этом во всех примерах передаётся один аргумент. Когда аргументов несколько, они обязательно разделяются запятыми.
Задание
Вам доступна функция calculate_distance_between_towns()
. Она принимает один аргумент, в котором должны содержаться названия двух городов через дефис. В ответ она возвращает расстояние между этими городами. Вот пример использования:
distance = calculate_distance_between_towns('Lannisport-Bayasabhad')
Напишите программу, которая использует функцию calculate_distance_between_towns()
и выводит на экран расстояние между городами, записанными в переменные source
и destination
.
37. Вызов функций в аргументах функций
Python: Вызов функций в аргументах функций
Продолжаем тему выражений. Как вы помните, вызов функции — выражение, а значит мы можем положить вызов функции в вызов функции (…в вызов функции в вызов функции в вызов… а-а-а!).
number = -100.234203
result = round(abs(number), 2) # 100.23
Мы вызываем функцию round()
и передаем ей два аргумента:
- результат вызова функции
abs()
с аргументомnumber
- число
2
.
Можно сделать то же самое, но с промежуточными шагами:
number = -100.234203
module = abs(number) # 100.234203
result = round(module, 2) # 100.23
Какой вариант предпочтительнее? Если вычисление совсем простое и неглубокое (не больше одного вложения функции), то можно смело вкладывать вызов в вызов. В остальных ситуациях предпочтительно разбивать вызовы на промежуточные вычисления.
Причины все те же. Чтение такого кода значительно легче. Во-первых, промежуточные переменные своими названиями отражают суть операций. Во-вторых, такой код легче отлаживать, а промежуточные данные проще исследовать. И в-третьих, глубокие вложенные вызовы сложно читать. В продвинутых языках подобная проблема решается механизмом типа композиции функций, но в Python, к сожалению, подобного нет.
Давайте взглянем на код и попробуем ответить на вопрос: что в каком порядке будет вычисляться?
number = -100.234203
result = round(abs(number), round(2.43))
Python, как и большинство традиционных языков, является языком с энергичными вычислениями (в других языках бывают ленивые вычисления). Python пытается вычислить сначала максимально глубокий уровень вызова, затем менее глубокий, и так двигается снизу вверх пока не вычислит всё.
В нашем примере сначала будут вычислены аргументы, а затем получившиеся данные попадут в вызов round()
.
Ситуация с вложенными вызовами функций часто вводит новичков в ступор. Здесь нет никакой магии, нужно просто чуть больше тренировок. Хорошее упражнение — расписывать процесс по шагам на бумажке, симулируя действия компьютера. Вот так:
result = round(abs(number), round(2.43))
result = round(100.234203, round(2.43))
result = round(100.234203, 2)
result = 100.23
Задание
Для построения генеалогического дерева семьи Старков Сэм написал функцию parent_for()
, которая возвращает имя родителя, если передать ей первым параметром имя ребенка. Вторым параметром функция принимает строчку 'father'
или 'mother'
. Так функция понимает, кого из родителей возвращать. По умолчанию параметр равен 'mother'
. То есть, если нужно узнать имя матери, то можно не передавать специально 'mother'
, а передать лишь один параметр — имя ребенка.
Напишите программу, которая выводит на экран имя деда Джоффри по материнской линии. Полное имя Джоффри на английском: Joffrey Baratheon.
Пример использования функции parent_for()
:
mother = parent_for('Joffrey Baratheon')
print(mother) # => Cersei Lannister
38. Детерминированность
Python: Детерминированность
Независимо от того, какой язык программирования используется, функции внутри него обладают некоторыми фундаментальными свойствами. Зная эти свойства, легче прогнозировать поведение функций, способы их тестирования и место их использования. К таким свойствам относится детерминированность. Функция называется детерминированной тогда, когда для одних и тех же входных аргументов она возвращает один и тот же результат. Например, функция hex()
, которая возвращает шестнадцатеричное представление числа, детерминированная.
hex(255) # '0xff'
hex(255) # '0xff'
Сколько бы раз мы её не вызывали, передавая туда значение 255
, она всегда вернет '0xff'
(хотя технически можно написать её и по другому, но смысла в этом никакого, а проблем доставит). В свою очередь функция, возвращающая случайное число, не является детерминированной, так как у одного и того же входа (даже если он пустой, то есть аргументы не принимаются) мы получим всегда разный результат. Насколько он разный – не важно, даже если хотя бы один из миллиона вызовов вернет что-то другое, эта функция автоматически считается недетерминированной.
from random import random
random() # 0.5731815251322182
random() # 0.9703312707029572
Зачем это нужно знать? Детерминированность серьезно влияет на многие аспекты. Детерминированные функции удобны в работе, их легко оптимизировать, легко тестировать. Если есть возможность сделать функцию детерминированной, не раздумывая делайте её такой.
Задание
Санса хочет повесить на свою дверь просьбу о том, чтобы никто не входил без стука. Она попросила Сэма распечатать лист с надписью “СТУЧАТЬ!”. Помогите Сэму перевести слово в верхний регистр, используя функцию to_upper_case()
, которая принимает на вход строку и возвращает такую же, но со всеми буквами в верхнем регистре. Распечатайте на экран текст, записанный в константу text
, не забыв перевести его в верхний регистр.
from hexlet.code_basics import to_upper_case
print(to_upper_case('hello')) # => HELLO
Как вы думаете, что вернет функция to_upper_case()
, если передать ей на вход строку HELLO?
Советы
- Детерминированные функции
Определения
- Детерминированная функция – Функция, вызов которой с одними и теми же аргументами всегда даёт один и тот же результат
39. Побочные эффекты
Python: Побочные эффекты
Вы, скорее всего, уже заметили (может, подсознательно), что print()
— это тоже функция. Она принимает на вход данные любого типа и выводит их на экран.
Внимание, вопрос: что возвращает функция print()
?
Ответ: что бы она не возвращала, это значение никак не используется.
print()
выводит что-то на экран, но это не возврат значения, это просто какое-то действие, которое выполняет функция.
Вывод на экран и возврат значения из функции — разные и независимые операции. Технически вывод на экран равносилен записи в файл (немного особый, но все таки файл). Для понимания этой темы необходимо немного разобраться в устройстве операционных систем, что крайне важно для программистов.
С точки зрения программы вывод на экран — это так называемый побочный эффект. Побочным эффектом называют действия, которые изменяют внешнее окружение (среду выполнения). К таким действиям относятся любые сетевые взаимодействия, взаимодействие с файловой системой (чтение и запись файлов), вывод информации на экран или печать на принтере и так далее.
Побочные эффекты — один из основных источников проблем и ошибок в программных системах. Код с побочными эффектами сложен в тестировании, ненадежен. При этом без побочных эффектов программирование не имеет смысла. Без них было бы невозможно получить результат работы программы (записать в базу, вывести на экран, отправить по сети и так далее).
Понимание принципов работы с побочными эффектами очень сильно влияет на стиль программирования и способность строить качественные программы. Эта тема полностью раскроется в курсах на Хекслете.
Вопрос для самопроверки. Можно ли определить наличие побочных эффектов внутри функции, опираясь только на её возврат?
Задание
Это задание не связано напрямую с уроком. Но выполнить его без создания переменных — важный шаг в вашем профессиональном развитии.
Выведите на экран имя матери Дайнерис Таргариен (Daenerys Targaryen), используя функцию parent_for()
без создания переменных.
Напомним, что parent_for()
принимает первым параметром имя ребенка и возвращает имя родителя. Вторым параметром функция принимает строчку 'father'
или 'mother'
. Так функция понимает, кого из родителей возвращать. По умолчанию параметр равен 'mother'
.
Советы
- Побочный эффект
Определения
- Побочный эффект – действие, которое изменяет внешнее окружение (среду выполнения). Например, вывод на экран или отправка письма.
- Чистые функции – Детерминированные функции без побочных эффектов
40. Неизменяемость и примитивные типы
Python: Неизменяемость и примитивные типы
Рассмотрим функцию abs()
, которая делает число неотрицательным:
balance = -200
amount = abs(balance)
print(amount) # => 200
На экран выведется 200
.
Но что будет выведено на экран, если вызвать print(balance)
? Выведется старое значение: -200
.
Функция abs()
вернула новые данные, но не изменила переданные в неё данные. Она не могла это сделать физически, потому что примитивные типы в Python — неизменяемы.
(Напомним, что примитивные типы — это простые типы, встроенные в сам язык программирования: например, число или строка).
В будущих уроках и заданиях мы будем использовать как собственные функции, так и функции из стандартной библиотеки, но никакие функции не смогут изменять данные примитивных типов.
Число -200
— это значение переменной balance
, и само число нельзя изменить. Но переменная называется переменной, потому что её значение можно заменить на другое значение.
То есть мы можем написать:
balance = -200
balance = abs(balance)
print(balance)
Сначала в переменную записывается одно значение, а потом в ту же переменную вместо предыдущего значения записывается новое: то, что вернет вызов abs(balance)
.
Строку balance = abs(balance)
можно прочитать так: «записать в переменную balance
то, что вернет вызов функции abs()
, если передать в неё текущее значение переменной balance
».
Мы не изменили число, мы изменили переменную — записали в неё новое число вместо старого.
Изменение уже существующей переменной может показаться безобидным действием. Но в реальных программах переписывание переменной становится источником проблем. Код с изменяемыми переменными сложно понимать и анализировать: никогда нельзя быть уверенным, какое значение будет у переменной в какой-то момент времени.
Вы наверняка регулярно сталкиваетесь с багами и ошибками в приложениях, которые используете. Многие вызваны именно обилием изменяемых переменных: такие ошибки сложно найти и исправить.
Единственное место, где без переменных никак — это циклы, с которыми мы познакомимся позже. Во всех остальных местах относитесь к переменным как к константам — неизменяемым сущностям. Создавайте переменные, задавайте им значение и больше не меняйте.
Задание
Переведите число, записанное в переменную value
, в шестнадцатиричный вид, используя функцию hex()
. Новое значение (а это уже будет строка!) запишите в ту же переменную value
.
Возможно, вам покажется, что код получился странным. Это типичный пример: переписывание переменных делает код менее понятным и более запутанным, особенно в таких случаях, как этот — когда новое значение имеет тип, отличающийся от типа старого значения.
Определение функций
Определение собственных функций значительно упрощает написание и поддержку программ. Например, умение определять функции позволяет объединять сложные (составные) операции в одну – вся сложность может быть скрыта за одной простой функцией. Научившись писать функции, вы сделаете первый шаг на пути к построению по-настоящему полезных программ. И мы вам в этом поможем. В этом модуле вы создадите свою первую функцию и научитесь давать ей название, которое поймет любой разработчик.
41. Создание (определение) функции
Python: Создание (определение) функции
Пора научиться создавать собственные функции! Код, в котором создаётся функция, называется определением функции.
Вот шаблон определения простой функции:
def имя_функции():
# тело функции, т.е. код
print("abc")
Определение собственных функций значительно упрощает написание и поддержку программ. Функции позволяют объединять сложные (составные) операции в одну. Например, отправка письма на сайте – это достаточно сложный процесс, включающий в себя взаимодействие с внешними системами (интернет). Благодаря возможности определять функции, вся сложность может быть скрыта за простой функцией:
from some-email-package import send
email = '[email protected]'
title = 'Помогите'
body = 'Я написал историю успеха, как я могу получить скидку?'
# Один маленький вызов — и много логики внутри
send(email, title, body)
Здесь мы в первый раз сталкиваемся с новым элементом синтаксиса — блоками инструкций. В Python несколько инструкций, объединённых по смыслу в некотоpyю группу – блок – записываются с отступом в четыре пробела. Блок всегда заканчивается перед первой строчкой, которая имеет отступ меньший, чем строчки блока. В примере выше тело функции — это блок.
Создадим нашу первую функцию. У неё будет одна задача: выводить на экран текст Today is: December 5
.
Нужно назвать функцию так, чтобы из названия была понятна её задача. Давайте дадим ей имя show_date()
:
# Определение функции
# Определение не вызывает функцию
# Мы лишь говорим, что теперь такая функция существует
def show_date():
text = 'Today is: December 5'
print(text)
В нашей функции только две строчки кода, но их может быть сколько угодно. Функции можно считать программами внутри программ.
Чтобы на 100% понять происходящее, обязательно сделайте следующее:
- Зайдите на https://repl.it/languages/python3. Это онлайн-интерпретатор Python.
- В левой части введите код примера выше.
- Запустите программу нажатием на “RUN ▶”.
- Программа выполнится, но на экран ничего не выведется, потому что в программе есть только определение функции, но не запуск.
- Чтобы запустить функцию, нужно вызвать её. Добавьте к программе вызов:
show_date() # => Today is: December 5
- Запустите программу снова и удостоверьтесь, что в правой части на экран вывелся текст.
Соберём всё вместе. Вот полная программа, с определением функции и вызовом:
def show_date():
text = 'Today is: December 5'
print(text)
show_date()
Today is: December 5
Понятие «создать функцию» имеет много синонимов: «реализовать», «определить» и даже «заимплементить» (от слова implement). Все они встречаются в повседневной практике на работе.
Задание
Реализуйте функцию print_motto()
, которая печатает на экран фразу Winter is coming.
print_motto() # => Winter is coming
Важное замечание! В задачах, в которых нужно реализовать функцию, эту функцию вызывать не нужно. Вызывать функцию будут автоматизированные тесты, которые проверяют ее работоспособность. Пример с вызовом выше показан только для того, чтобы вы понимали, как ваша функция будет использоваться.
Советы
- Именование в программировании
42. Передача одного аргумента
Python: Передача одного аргумента
Функции без аргументов встречаются редко. Чаще функции принимают на вход данные, как-то их используют и выдают результат обратно. В этом уроке мы познакомимся с определением функций, принимающих на вход один аргумент. Посмотрите на определение ниже:
def show_date(text):
print("Today is: " + text)
show_date('January 29')
Today is: January 29
Вот иллюстрация того, как аргумент из вызова попадает в тело функции и используется там:
Теперь понятно, зачем нужны были круглые скобки после имени функции: в них можно указать аргументы. Теперь наша функция принимает один аргумент — text
.
Заметьте: мы не создаём переменную text
, но используем её в теле функции. Python работает так: переменная сама создаётся при вызове и указанное значение (в нашем примере — 'January 29'
) записывается в эту переменную.
Аргументы можно называть как угодно, их имена имеют смысл исключительно в теле функции. Например, если изменить имя аргумента так:
def show_date(lala):
print('Today is: ' + lala)
bla = 'January 29'
show_date(bla)
то поведение функции не изменится. Это касается как имён внутри функции (lala
), так и снаружи (bla
). Но такое бессмысленное имя для аргумента — плохая практика.
Новички иногда пытаются сделать примерно такое определение функции:
def show_date('Today is: December 5'):
# какой-нибудь код
Запустить код не получится — он содержит синтаксическую ошибку. Вместо переменной в аргументе написана строка, то есть значение.
Аргумент должен быть переменной, иначе он не сможет быть аргументом, то есть чем-то, что принимает значение при вызове.
Если же вам нужна какая-то информация в функции, и вы заранее знаете какая именно, то аргумент для этого не нужен, ведь мы уже умеем сохранять значения для последующего использования — достаточно создать переменную в самом теле:
def show_date():
text = 'Today is: December 5'
# какой-нибудь код
Задание
Реализуйте функцию print_jaimes_line()
, которая принимает один аргумент — строку, и выводит реплику на экран в формате JAIME: переданная_строка
.
Как назвать переменную, которая будет аргументом — решайте сами.
Наша система содержит код, скрытый от вас. В этом упражнении скрыт вызов функции print_jaimes_line()
. Так мы проверяем ваше решение.
Вам не нужно самостоятельно вызывать функцию, только определить её. Вот как наша система будет вызывать её:
print_jaimes_line("Farewell, my friend...")
JAIME: Farewell, my friend...
Повторяю, не торопитесь выводить это сообщение самостоятельно, иначе ваша программа не пройдёт тесты! Только объявите функцию!
43. Передача нескольких аргументов
Python: Передача нескольких аргументов
Аргументов может быть несколько. В таком случае в определении функции мы делаем то же самое, что в вызове: просто указываем аргументы через запятую.
Полный пример определения функции с несколькими аргументами и её вызова:
def show_date(month, day):
print('Today is: {} {}'.format(month, day))
show_date('January', '29')
Today is: January 29
Главное — помнить про порядок: в каком порядке аргументы стоят при определении функции, в таком же порядке они должны передаваться при вызове.
В Python, как и в большинистве языков, при вызове функции нужно обязательно передать столько же аргументов, сколько было указано при её определении. Иначе программа выдаст ошибку:
show_date('January')
# => TypeError: show_date() missing 1 required positional argument: 'day'
show_date()
# => TypeError: show_date() missing 2 required positional arguments: 'month' and 'day'
Задание
Сэм составляет множество карт, и ему часто нужно выводить на экран повторяющиеся символы для визуализации маршрутов. Например, так Сэм иллюстрирует узкие дороги между городами:
Meereen =-=-=-=- Myr
А так иллюстрирует широкие трассы:
Vaes Dothrak ======== Vahar
Строки в Python — это последовательности (списки, наборы) символов в нужном порядке. В документации Python приведены операции, общие для всех последовательностей. Так, любую последовательность — в том числе и строку — можно повторить несколько раз, умножив оную на число (это не будет нарушением типизации, ведь именно такое сочетание типов — последовательность слева и число справа — предусмотрено создателями языка для операции умножения):
print('=-' * 4)
=-=-=-=-
Сэм не очень доволен. Ему нужно нарисовать сотни маршрутов разной длины с разными символами. Неудобно размножать строки сотни раз и сотни раз вызывать print
.
Напишите для Сэма функцию print_seq()
, которая сама выводит на экран получившиеся повторения. Она принимает два аргумента — строку и число, и выводит повторяющуюся строку на экран. Для генерации строки используйте “размножение” строк.
Вот пример того, как Сэм будет использовать написанную вами print_seq()
:
print_seq('=-', 4)
=-=-=-=-
44. Возврат значений
Python: Возврат значений
В модуле «Вызов функций» мы в основном работали с функциями, которые выводят результат на экран, а не возвращают его. Честно говоря, вывод на экран — фактически обучающий элемент. В реальном коде на экран никто ничего не выводит (за исключением утилит командной строки). Функции возвращают данные, которые потребляются другими функциями.
Научиться писать функции, которые возвращают информацию — первый шаг на пути к построению по-настоящему полезных программ.
Начнем с тривиального примера: создадим и вызовем функцию, которая принимает два числа и возвращает первое число минус второе. Назовём её sub
, от англ. “subtract” — «вычесть»:
def sub(a, b):
result = a - b
return result
print(sub(10, 7))
3
Обратите внимание: мы знаем, что вызов функции — выражение, поэтому мы передали вызов одной функции в вызов другой функции — print(sub(10, 7))
.
Возврат задаётся специальной инструкцией return
. Cправа от return
помещается выражение. Любое выражение. То есть, мы можем делать вычисления сразу после return
без создания переменной result
:
def sub(a, b):
# Сначала вычисляется выражение справа от `return`
# затем получившееся значение возвращается
return a - b
print(sub(2018, 1975))
43
После исполнения функции возвращенный результат «встаёт» на место вызова:
Интерпретатор, встречая return
, останавливает дальнейшее выполнение функции и возвращает указанное справа значение в то место, где была вызвана функция.
Посмотрите на эту функцию:
def foo():
return 7
return 10
print(foo())
Что выведется на экран?
Правильный ответ: 7
. Функция всегда будет возвращать только число 7
, так как интерпретатор, наткнувшись на первый return
, остановит выполнение функции. Строчка кода return 10
никогда не выполнится.
Задание
Сэм создаёт генеалогические деревья разных семей. Ему постоянно приходится рассчитывать количество места, занимаемое именами родителей на экране.
Создайте функцию get_parent_names_total_length()
для Сэма. Она должна принимать один аргумент — имя ребенка, и возвращать количество символов в именах матери и отца суммарно. Функция не должна выводить ничего на экран, только возвращать число.
get_parent_names_total_length('Daenerys Targaryen') # 35
Для реализации используйте уже существующие функции parent_for()
и len()
:
- Получение имени матери
parent_for(child, 'mother')
, гдеchild
— имя ребёнка. - Получение имени отца
parent_for(child, 'father')
, гдеchild
— имя ребёнка - Получение длины строки:
len(str)
, гдеstr
— строка
Вам не нужно вызывать свою функцию, только определить её.
45. Возврат по умолчанию
Python: Возврат по умолчанию
Рассмотрим немного модифицированную функцию из предыдущего урока:
def sub(a, b):
# Полученный результат никак не используется
# и не возвращается наружу
answer = a - b
result = sub(10, 7)
print(result)
Этот код не содержит синтаксических ошибок, и интерпретатор выполнит его. Но на экран выведется строка None
! В функции нет return
, но несмотря на это, код отработал и в переменную result
записалось что-то.
Держитесь, мы снова повторяем мантру: вызов функции — это выражение, а выражения всегда возвращают результат своего выполнения. Значит, функция всегда возвращает что-то.
Во многих языках программирования есть специальный тип данных с единственным значением: None
(или null
, nil
— названия типа и значения в разных языках различаются). Этот тип используется в ситуациях, когда значение чего-либо не определено.
Так вот, если в функции нет инструкции return
, то интерпретатор Python автоматически возвращает None
. Поэтому в переменную result
записался None
.
Но, конечно, смысла в этой программе нет — функция сейчас не делает ничего полезного. Давайте добавим return
:
def sub(a, b):
answer = a - b
return answer
result = sub(10, 7)
print(result)
Теперь всё работает как надо.
Забыть инструкцию return
— частая ошибка новичка. Мы в обучении каждый день сталкиваемся с просьбами о помощи типа «функция правильная, но почему-то не работает». И почти всегда оказывается, что забыт return
, а результат, вместо возврата, просто печатается на экран.
Кстати, вы можете сами написать return None
в теле функции. Функция будет возвращать None
, как если бы в ней не было return
.
Вопрос для самопроверки. Что возвращает функция print()
?
Задание
Это немного странное задание, но для тренировки будет полезным. Реализуйте функцию get_none()
, не имеющую параметров и всегда возвращающую None
. Больше функция ничего делать не должна — вот такая она странная! И возвращайте None
самостоятельно, не ждите, что Python это сделает за вас — в этом и суть данной тренировки!
Вам не нужно вызывать свою функцию, только определить её.
46. Параметры по умолчанию
Python: Параметры по умолчанию
Напомним, что аргумент может быть необязательным. У такого аргумента есть значение по умолчанию.
Например, функция parent_for()
, которую вы использовали в некоторых упражнениях, принимает имя ребёнка первым аргументом, а вторым — строку 'mother'
или 'father'
. Второй аргумент — необязательный, и если не указывать его, то автоматически по умолчанию будет использоваться 'mother'
.
Эти два вызова равнозначны:
parent_for('Jon Snow')
parent_for('Jon Snow', 'mother')
Каким образом там сделаны аргументы по умолчанию? Давайте заглянем в определение этой функции:
def parent_for(child_name, parent_name='mother'):
# какой-то код
Первый аргумент указан привычно — просто название переменной. Это делает аргумент обязательным.
Второй аргумент указан со значением в формате аргумент=какое_то_значение
. Точно так же, как при создании переменной. Этот фрагмент =какое_то_значение
делает аргумент необязательным, и задаёт ему значение по умолчанию.
Аргументов по умолчанию может быть любое количество, но все они должны быть в конце списка аргументов. То есть такие строчки кода синтаксически некорректны:
def parent_for(child_name='Jon', who):
def calculate(a, b=90, c):
def get_prices(code=4161, quantity, place):
Задание
Реализуйте функцию custom_parent_for()
, которая принимает два аргумента:
- Строку — имя ребёнка.
- Строку с указанием родителя. Этот аргумент должен по умолчанию быть
'father'
.
Функция должна возвращать имя соответствующего родителя.
Пример вызова:
custom_parent_for('Cersei Lannister') # Tywin Lannister
Такой вызов вернёт имя отца.
- Используйте готовую функцию
parent_for()
внутри своей функции. - Не нужно вызывать свою функцию
custom_parent_for()
, только определить её.
47. Именование
Python: Именование
Стиль именования функций в Python такой же, как и стиль именования переменных: snake_case. Но при выборе самих слов есть важное отличие.
Функция — действие, вызов функции всегда подобен указанию «сходи», «возьми», «напечатай», «положи» и так далее. Вспомните, какие функции были в предыдущих уроках:
show_date()
(«показать дату»)sub()
(subtract — «вычесть»)print()
(«напечатать»)round()
(«округлить»)
Переменная — сущность, поэтому мы используем существительные:
child
result
euros
Берите на вооружение следующую структуру: функция — глагол, переменная — существительное.
В уроке про переменные мы просили вас придумать название переменной и записать в блокноте или отправить себе на почту. Найдите это название и посмотрите на него свежим взглядом: оно понятное? Описывает суть однозначно или требует вникания?
(У нас также была функция parent_for()
, и это имя — не глагол. Можно было бы назвать её get_parent_for()
, но имена типа parent_for()
, child_of()
, price_at()
— общепринятые формы. «Функция — глагол» — не железное правило, но почти всегда лучше придерживаться его).
Жизнь программиста наполнена такими моментами: открыть старый код и попытаться понять его. Будьте добры к будущему себе и к коллегам, давайте переменным и функциям понятные названия.
Задание
В Python из языка Си перекочевал ещё один способ форматирования (интерполяции) строк (в дополнение к .format()
, который мы рассматривали ранее в разделе про интерполяцию строк). Оператор форматирования %
coздаёт строку на основе шаблона и данных:
result = 'Today is %s %d' % ('February', 8)
print(result)
Today is February 8
Слева от оператора %
находится строка-шаблон, в которой кроме самого текста могут присутствовать специальные заполнители. Это «заглушки» для информации, которая передаётся аргументом справа. %s
означает «заглушка для строки», %d
— для числа. Поэтому справа находится пара значений — строка и число.
Значения записаны в общих скобках через запятую: это так называемый кортеж — фиксированной длины набор значений. На английском “кортеж” звучит как “tuple”. В конце урока вы найдёте ссылку на статью о кортежах в информатике.
Порядок и типы значений должны совпадать с порядком и типами заглушек.
При выводе дат иногда требуется фиксировать количество цифр, скажем, всегда писать нули перед числом если число меньше 10. %
позволяет решить эту задачу:
result = 'Today is %s %02d' % ('February', 8)
print(result)
Today is February 08
%02d
— сделать две цифры и заполнить нулями оставшееся пространство. %03d
— три цифры, и так далее:
result = 'Today is %s %04d' % ('February', 8)
print(result)
Today is February 0008
Реализуйте функцию get_formatted_birthday()
, которая принимает на вход три параметра: день, месяц и год рождения, а возвращает их строкой в отформатированном виде, например: 30-02-1953
.
result = get_formatted_birthday(30, 2, 1953)
print(result)
30-02-1953
День и месяц нужно форматировать так, чтобы при необходимости добавлялся 0
слева. Например, если в качестве месяца пришла цифра 7
, то в выходной строке она должна быть представлена как 07
.
Советы
- Кортежи в информатике
- Статья про именование в программировании
- Ментальное программирование
48. Окружение
Python: Окружение
Какое значение окажется внутри переменной result
после выполнения кода?
age = 5
def generate():
return age + 3
result = generate()
Правильный ответ: в result
будет записано число 8
.
Хоть переменная age
и не является аргументом функции generate
, она всё равно «видна» в теле функции. Точно так же будут видны и любые другие переменные, если они определены выше определения функции (т.е. определены ранее, ведь интерпретатор Python читает файл сверху-вниз).
Рассмотрим другой пример:
age = 5
def generate():
age = 10
return age + 3
result = generate()
В данном случае результатом будет число 13
. Внешнее значение age = 5
никак не влияет на код функции, потому что в теле функции определена «своя версия» переменной age
— так называемая локальная переменная. Локальные переменные не видны за пределами функции.
И последний пример:
age = 5
def generate():
age = 8
generate()
result = age
Ответ: 5.
Локальная переменная, созданная внутри функции generate
никак не влияет на внешнюю переменную age
, поэтому после вызова функции значение внешней age
не изменилось и осталось 5
.
Задание
Это задание не связано напрямую с уроком, это просто еще одно полезное упражнение по работе с функциями.
Напишите функцию get_age_difference()
, которая принимает два года рождения и возвращает строку с разницей в возрасте в виде The age difference is 11
. Например:
actual = get_age_difference(2001, 2018)
print(actual) # => 17
Советы
- Напомним, что в Python есть функция
abs
, которая возвращает модуль переданного числа: например,abs(-12)
вернёт12
.
Логика
Логические выражения позволяют отвечать на вопросы, которые возникают во время работы программы. Пользователь аутентифицирован? Подписка оплачена? Год високосный? В этом модуле изучаем функции-предикаты – те, которые задают вопрос и отвечают на него – правда это или ложь. Попрактикуемся в написании таких функций и перейдем к более сложным логическим выражениям.
49. Логический тип
Python: Логический тип
Кроме арифметических операций, со школы нам известны операции сравнения. Например, 5 > 4
. Это звучит как вопрос: «5 больше 4?». В данном случае ответ «да». В других случаях ответом может быть «нет» (например, для 3 < 1
).
Операции сравнения не имеют привязки к числам. Сравнивать можно практически всё что угодно, например, строки. Каждый раз, когда мы входим на какой-то сайт, внутри происходит сравнение введенных логина и пароля с теми, какие есть в базе. И только если они есть, нас пускают во внутрь (аутентифицируют).
Языки программирования адаптировали все математические операции сравнения практически в неизменном виде. Единственное серьезное отличие – операторы равенства и неравенства. В математике для этого используется обычное равно =
, но в программировании такое встречается не часто. Во многих языках символ =
используется для присваивания значений переменным, поэтому для сравнения в Python взяли ==
.
Список операций сравнения:
<
— меньше<=
— меньше или равно>
— больше>=
— больше или равно==
— равно!=
— не равно
Эти операции применимы не только к числам. Например, с помощью оператора равенства можно сравнить строки:
password == text
— это сравнение идентичности строк, записанных в разных переменных.
Логическая операция типа 5 > 4
или password == text
— это выражение, и его результат — специальное значение True
(«истина») или False
(«ложь»). Это новый для нас тип данных — bool
.
result = 5 > 4
print(result) # => True
print('one' != 'one') # => False
Наряду со строками (str
) и целыми и рациональными числами, тип bool
(булев) — это один из примитивных типов данных в Python.
Попробуем написать простую функцию, которая принимает на вход возраст ребёнка и определяет, младенец ли он. Младенцами считаются дети до года:
def is_infant(age):
return age < 1
Пользуемся тем фактом, что любая операция — это выражение, поэтому единственной строчкой функции пишем «вернуть то значение, которое получится в результате сравнения age < 1
».
В зависимости от пришедшего аргумента, сравнение будет либо истинным (True
), либо ложным (False
), а return
вернёт этот результат.
Вызовем функцию и выведем на экран результат:
def is_infant(age):
return age < 1
print(is_infant(3))
False
А теперь проверим ребенка, которому полгода:
print(is_infant(0.5))
True
Задание
Напишите функцию is_pensioner()
, которая принимает возраст в качестве единственного аргумента и проверяет, является ли этот возраст пенсионным. Пенсионным считается возраст 60 лет и больше.
Примеры вызова:
is_pensioner(75) # True
is_pensioner(18) # False
Определения
- Логический тип (bool) – тип данных с двумя возможными значениями:
True
(истина) иFalse
(ложь).
50. Предикаты
Python: Предикаты
Вспомним функцию is_infant()
из прошлого урока:
def is_infant(age):
return age < 1
print(is_infant(3))
False
is_infant()
— это функция-предикат (или функция-вопрос).
Предикат отвечает на утвердительный вопрос «да» или «нет», возвращая значение типа bool.
Предикаты во всех языках принято именовать особым образом для простоты анализа. В Python предикаты, как правило, начинаются с префикса is
или has
:
is_infant()
— «младенец ли?»has_children()
— «есть ли дети?»is_empty()
— «пустой ли?»has_errors()
— «есть ли ошибки?»
Функция может считаться предикатом, только если она возвращает bool
.
Давайте напишем еще одну функцию-предикат. Она принимает строку и проверяет, является ли она словом 'Castle'
:
def is_castle(string):
return string == 'Castle'
print(is_castle('Sea'))
False
Задание
Напишите функцию is_mister()
, которая принимает строку и проверяет, является ли она словом 'Mister'
.
is_mister('Mister') # True
is_mister('Missis') # False
Советы
- Именование в программировании
Определения
- Предикат – выражение, отвечающее на вопрос «да» или «нет» с помощью типа bool.
51. Комбинирование операций и функций
Python: Комбинирование операций и функций
Логические операции — это выражения. Значит, логические операции можно комбинировать с другими выражениями.
Например, мы хотим проверить чётность числа, то есть кратность двум. В программировании используют такой подход: проверяют остаток от деления на 2:
- если остаток 0, то число было чётным;
- если остаток не 0, то число было нечётным.
Остаток от деления — простая, но очень важная концепция в арифметике, алгебре, и даже в теории чисел и криптографии. Идея проста: нужно разделить число на несколько равных групп, и если в конце что-то останется — это и есть остаток от деления.
Делим конфеты поровну между людьми:
- 7 конфет, 2 человека: 2 x 3 + остаток 1. Значит, 7 не кратно 2.
- 21 конфету, 3 человека: 3 x 7 + остаток 0. Значит, 21 кратно 3.
- 19 конфет, 5 человек: 5 x 3 + остаток 4. Значит, 19 не кратно 5.
Оператор %
вычисляет остаток от деления (не путайте с делением):
7 % 2
→1
21 % 3
→0
19 % 5
→4
С помощью него напишем функцию проверки чётности:
def is_even(number):
return number % 2 == 0
print(is_even(10)) # => True
print(is_even(3)) # => False
В одном выражении мы скомбинировали логический оператор ==
(проверка равенства) и арифметический оператор %
.
Приоритет арифметических операций выше логических. Значит, сначала вычисляется арифметическое выражение number % 2
, затем результат участвует в логическом сравнении.
По-русски это можно расшифровать так: «вычислить остаток от деления числа number
на 2 и сравнить, равен ли остаток нулю, затем вернуть результат проверки равенства».
Другой пример: напишем функцию, которая принимает строку и проверяет, начинается ли эта строка с латинской буквы a
.
Алгоритм:
- Получим и запишем в переменную первый символ из строки-аргумента.
- Сравним, равен ли символ латинской букве
a
. - Вернём результат.
def is_first_letter_an_a(string):
first_letter = string[0]
return first_letter == 'a'
print(is_first_letter_an_a('orange')) # => False
print(is_first_letter_an_a('apple')) # => True
Попробуйте проговорить происходящее по-русски, аналогично тому, как мы расшифровывали процесс в примере с is_even()
в начале урока.
Напомним об извлечении символов из строки с помощью квадратных скобок:
first_name = 'Alexander'
first_name[0] # 'A'
first_name[1] # 'l'
first_name[2] # 'e'
first_name[3] # 'x'
first_name[4] # 'a'
first_name[5] # 'n'
first_name[6] # 'd'
first_name[7] # 'e'
first_name[8] # 'r'
Задание
Сэм решил изучить историю Таргариенов со времени первых людей, но книг было много и информация могла находиться в любой из них. К счастью для Сэма, большинство книг были оцифрованы молодыми мейстерами. Он подумал, что неплохо бы написать функцию, которая анализирует тексты на наличие в них упоминания фамилии Таргариенов.
Для выполнения задания вам потребуется способ, позволяющий отрезать от строки, начиная от её начала, кусок заданной длины. Сделать это легко, используя слегка изменённый вариант операции извлечения символа:
string[i:j]
Здесь i
— порядковый номер (индекс) символа, начиная с которого будет производиться “обрезка” строки; j
— индекс символа, перед которым отрезок должен закончиться. Длина отрезка составит j - i
. Примеры:
print('Winterfell'[0:6]) # => 'Winter'
print('Stark'[0:6]) # => 'Stark'
# берём отрезок, начиная с символа с индексом 2
# и заканчивая символом с индексом 4
# (т.е. "перед символом с индексом 5")
print('Absolute'[2:5]) # => 'sol'
# начиная с символа с индексом 3
# и не доходя до символа с индексом 3,
# берём пустой отрезок (3 - 3 == 0)
print('Absolute'[3:3]) # => ''
(во втором примере 'Stark'[0:6])
символов получилось меньше, чем мы хотели, ведь в изначальной строке было меньше шести символов).
Есть укороченный синтаксис для этой операции, когда можно не указывать значение i
:
string[:j]
В таком случае, по умолчанию подставляется значение 0
— то есть считается, что отрезок берётся от начала строки до символа с индексом j
, не включая оный. Поэтому пару примеров выше мы можем переписать так:
print('Winterfell'[:6]) # => 'Winter'
print('Stark'[:6]) # => 'Stark'
И, естественно, вместо чисел можно использовать переменные:
n = 6
print('Winterfell'[:n]) # => 'Winter'
print('Stark'[:n]) # => 'Stark'
Реализуйте функцию has_targaryen_reference()
, которая принимает на вход строку и проверяет, начинается ли она с Targaryen. Эта функция должна извлекать подстроку такой же длины, как и у слова Targaryen, а затем проверять, равна ли извлечённая подстрока строке Targaryen. Напомню, что индексы начинаются с нуля.
print(has_targaryen_reference('')) # => False
print(has_targaryen_reference('Targari')) # => False
print(has_targaryen_reference('targaryen')) # => False
print(has_targaryen_reference('Targaryen')) # => True
52. Логические операторы
Python: Логические операторы
Мы уже умеем писать функции, которые проверяют одиночные условия. Теперь научимся строить составные условия.
Хороший пример: проверка пароля. Предположим, что некий сайт при регистрации требует, чтобы пароль был длиннее восьми символов и короче двадцати символов. Да, ограничение выглядит странно, но бывает и такое.
В математике мы бы написали 8 < x < 20
, но во многих языках программирования так сделать нельзя. К счастью, Python такие составные условия писать позволяет. И всё же на минутку мы забудем о такой возможности. Попробуем сделать два отдельных логических выражения и соединить их специальным оператором «И»:
Пароль длиннее 8 символов И пароль короче 20 символов.
Вот функция, которая принимает пароль и говорит, соответствует ли он условиям (True
) или не соответствует (False
):
def is_correct_password(password):
length = len(password)
return length > 8 and length < 20
print(is_correct_password('qwerty')) # => False
print(is_correct_password('qwerty1234')) # => True
print(is_correct_password('zxcvbnmasdfghjkqwertyui')) # => False
and
— означает «И» (в математической логике это называют конъюнкцией). Всё выражение считается истинным, только если истинен каждый операнд — каждое из составных выражений. Иными словами, and
означает «и то, и другое».
Приоритет этого оператора ниже, чем приоритет операторов сравнения, поэтому выражение length > 8 and length < 20
отрабатывает правильно без скобок.
Кроме and
часто используется оператор or
— «ИЛИ» (дизъюнкция). Он означает «или то, или другое, или оба». То есть выражение a or b
считается истинным, если хотя бы один из операндов (a или b или одновременно все операнды) является истинным. Иначе выражение ложное.
Операторы можно комбинировать в любом количестве и любой последовательности, но когда одновременно встречаются and
и or
, то приоритет лучше задавать скобками. Ниже пример расширенной функции определения корректности пароля:
def has_special_chars(str):
# проверяет содержание специальных символов в строке
def is_strong_password(password):
length = len(password)
# Скобки задают приоритет. Понятно что к чему относится.
return (length > 8 and length < 20) or has_special_chars(password)
Другой пример. Мы хотим купить квартиру, которая удовлетворяет условиям: площадь от 100 кв. метров и больше на любой улице ИЛИ площадь от 80 кв. метров и больше, но на центральной улице Main Street
.
Напишем функцию, проверяющую квартиру. Она принимает два аргумента: площадь (число) и название улицы (строку):
def is_good_apartment(area, street):
return area >= 100 or (area >= 80 and street == 'Main Street')
print(is_good_apartment(91, 'Queens Street')) # => False
print(is_good_apartment(78, 'Queens Street')) # => False
print(is_good_apartment(70, 'Main Street')) # => False
print(is_good_apartment(120, 'Queens Street')) # => True
print(is_good_apartment(120, 'Main Street')) # => True
print(is_good_apartment(80, 'Main Street')) # => True
Область математики, в которой изучаются логические операторы, называется булевой алгеброй. Ниже показаны «таблицы истинности» — по ним можно определить, каким будет результат применения оператора:
И and
A | B | A and B |
---|---|---|
True | True | True |
True | False | False |
False | True | False |
False | False | False |
ИЛИ or
A | B | A or B |
---|---|---|
True | True | True |
True | False | True |
False | True | True |
False | False | False |
Задание
Джон поручил Сэму реализовать автоматическое распознавание солдат Ланнистеров на видео. Идея автоматизировать дозор крепости казалась ему привлекательной. В процессе работы Сэму понадобилось написать функцию, которая определяет, Ланнистер ли перед ним или нет. Немного подумав, Сэм выделил следующие правила определения Ланнистера:
Если у солдата доспехи красного цвета И нет щита
ИЛИ
если у солдата есть щит с изображением льва
то это Ланнистер.
Напишите функцию is_lannister_soldier()
, которая принимает на вход два аргумента:
- Цвет доспехов (строка). Если доспехи красные, то строка
red
. None
, если щита нет. Строкаlion
, если щит есть, и на нём изображен лев.
Функция возвращает True
, если распознан Ланнистер, и False
, если не распознан.
Примеры вызова:
is_lannister_soldier('red', 'man') # False
is_lannister_soldier('blue', 'lion') # True
Когда будете проверять на равенство None
, делайте так, как принято в настоящем коде на Python: shield is None
— код будет выглядеть профессионально! Дело в том. что is
работает быстрее в случае некоторых специальных значений вроде None
, True
и False
.
Советы
- Булева алгебра
- Конъюнкция
- Дизъюнкция
Определения
- Логические операторы – операторы «И» (
and
), ИЛИ (or
), позволяющие создавать составные логические условия.
53. Отрицание
Python: Отрицание
Наряду с конъюнкцией (И) и дизъюнкцией (ИЛИ), часто используется операция «отрицание». Отрицание меняет логическое значение на противоположное.
В программировании ему соответствует унарный оператор not
.
not True # False
not False # True
Например, если есть функция, проверяющая чётность числа, то с помощью отрицания можно выполнить проверку нечётности:
def is_even(number):
return number % 2 == 0
print(is_even(10)) # => True
print(not is_even(10)) # => False
То есть мы просто добавили not
слева от вызова функции и получили обратное действие.
Отрицание — мощный инструмент, который позволяет лаконично выражать задуманные правила в коде без необходимости писать новые функции.
А что если написать так not not is_even(10)
? Удивительно, но код сработает:
print(not not is_even(10)) # => True
В логике двойное отрицание подобно отсутствию отрицания вообще:
not not True # True
not not False # False
print(not not is_even(10)) # => True
print(not not is_even(11)) # => False
Задание
Реализуйте функцию is_not_lannister_soldier()
, которая проверяет, что солдат — не Ланнистер. Функция принимает на вход такие же аргументы, как и функция is_lannister_soldier()
:
- Цвет доспехов (строка). Если доспехи красные, то строка
red
. None
если щита нет. Строкаlion
если щит есть, и на нём изображен лев.
Вам доступна уже готовая к использованию функция is_lannister_soldier
. Можете воспользоваться ей, чтобы не писать все логические условия заново.
Условия распознавания Ланнистера описаны в прошлом уроке.
Примеры вызова:
is_not_lannister_soldier('red', 'man') # True
is_not_lannister_soldier('blue', 'lion') # False
Советы
- Законы Де Моргана
54. Логические операторы
Python: Логические операторы 2
Логические операторы — важная тема, поэтому стоит закрепить её дополнительным примером и упражнением.
Попробуем реализовать функцию, проверяющую год на високосность. Год будет високосным, если он кратен 400 или он одновременно кратен 4 и НЕ кратен 100. Как видите, в определении уже заложена вся необходимая логика, осталось только переложить её на код:
def is_leap_year(year):
return year % 400 == 0 or (year % 4 == 0 and year % 100 != 0)
print(is_leap_year(2018)) # => False
print(is_leap_year(2017)) # => False
print(is_leap_year(2016)) # => True
Разберём по частям:
- первое условие
year % 400 == 0
: остаток от деления на 400 равен 0, значит, число кратно 400 or
ИЛИ- второе условие
(year % 4 == 0 and year % 100 != 0)
year % 4 == 0
: остаток от деления на 4 равен 0, значит, число кратно 4and
Иyear % 100 != 0
: остаток от деления на 100 не равен 0, значит, число не кратно 100
Задание
Напишите функцию is_neutral_soldier()
, которая принимает на вход два аргумента:
- Цвет доспехов (строка). Возможные варианты:
red
,yellow
,black
. - Цвет щита (строка). Возможные варианты:
red
,yellow
,black
.
Функция возвращает True
если цвет доспехов не красный и цвет щита чёрный. В остальных случаях возвращает False
.
Примеры вызова:
print(is_neutral_soldier('yellow', 'black')) # => True
print(is_neutral_soldier('red', 'black')) # => False
print(is_neutral_soldier('red', 'red')) # => False
Условные конструкции
Задача функции-предиката — получить ответ на вопрос, но обычно этого недостаточно и нужно выполнить определенное действие в зависимости от ответа. If и if-else – конструкции JavaScript, с помощью которых программист может выбирать необходимое поведение программы в зависимости от разных условий: пропускать одни инструкции и выполнять другие. Их и разберем на практике в этом модуле.
55. Условная конструкция
Python: Условная конструкция
Вкратце
if условие:
# код, который будет выполнен,
# если условие истинно
Задача предиката — получить ответ на вопрос. Но обычно этого не достаточно и нужно выполнить опредёленное действие в зависимости от ответа.
Напишем функцию, которая определяет тип переданного предложения. Для начала она будет отличать обычные предложения от вопросительных.
def get_type_of_sentence(sentence):
last_char = sentence[-1] # простой способ извлечь последний символ
if last_char == '?':
return 'question'
return 'normal'
print(get_type_of_sentence('Hodor')) # => normal
print(get_type_of_sentence('Hodor?')) # => question
if
— инструкция. В неё всегда передаётся выражение-предикат, а затем описывается блок кода (помним — блок описывается с отступом). Этот блок кода будет выполнен, только если выражение-предикат — истина.
Если предикат — ложь, то этот блок кода пропускается, и интерпретатор продолжит выполнять программу дальше. В нашем случае следующая строчка кода — return 'normal'
— заставит функцию вернуть строку и завершиться.
Как видите, return
может находиться где угодно в функции. В том числе внутри блока кода с условием.
Задание
Реализуйте функцию guess_number()
, которая принимает число и проверяет, равно ли число заданному (пусть это будет 42). Если равно, то функция должна вернуть строку 'You win!'
, в противном случае нужно вернуть строку 'Try again!'
.
Примеры вызова:
guess_number(42) # 'You win!'
guess_number(61) # 'Try again!'
Определения
- Условная конструкция – способ задать условие для выполнения кода. Например,
if x > 10: ...
56. else
Python: else
Попробуем изменить функцию из предыдущего примера так, чтобы она возвращала не просто тип предложения, а целую строку Sentence is normal
или Sentence is question
.
def get_type_of_sentence(sentence):
last_char = sentence[-1]
if last_char == '?':
sentence_type = 'question'
else:
sentence_type = 'normal'
return "Sentence is " + sentence_type
print(get_type_of_sentence('Hodor')) # => 'Sentence is normal'
print(get_type_of_sentence('Hodor?')) # => 'Sentence is question'
Мы добавили else
и новый блок. Этот блок выполнится, только если условие в if
— ложь. Также в блок else
можно вкладывать другие условия if
.
“Else” переводится «иначе», «в ином случае».
Существует два способа оформления конструкции if-else. С помощью отрицания можно изменить порядок блоков:
def get_type_of_sentence(sentence):
last_char = sentence[-1]
if last_char != '?':
sentence_type = 'normal'
else:
sentence_type = 'question'
return "Sentence is " + sentence_type
Какой способ предпочтительнее? Человеческому мозгу проще мыслить прямолинейно, а не через отрицание. Старайтесь выбирать проверку, которая не содержит отрицаний, и подстраивайте содержимое блоков под неё.
Задание
Реализуйте функцию normalize_url()
, которая выполняет так называемую нормализацию данных. Она принимает адрес сайта и возвращает его с https://
в начале.
Функция принимает адреса в виде АДРЕС
или http://АДРЕС
, но всегда возвращает адрес в виде https://АДРЕС
. На вход функции также может поступить адрес в уже нормализованном виде https://АДРЕС
, в этом случае ничего менять не надо.
Примеры вызова:
print(normalize_url('https://ya.ru')) # => 'https://ya.ru'
print(normalize_url('google.com')) # => 'https://google.com'
print(normalize_url('http://ai.fi')) # => 'https://ai.fi'
Есть два пути решения:
- Можно сравнить первые 7 символов строки-аргумента со строкой
http://
. - Можно использовать оператор
in
, чтобы проверить, содержится ли подстрока слева в строке справа. Используется он так:
print('cat' in 'Moscato') # => True
А потом на основе этого добавлять или не добавлять https://
.
Вам скорее всего потребуется отбросить ненужный кусок строки в начале оной. А помните, мы рассматривали способ получения кусочка от строки в её начале? Напоминаю:
# Берём 6 символов от начала
print('Winterfell'[:6]) # => 'Winter'
Так вот, можно отбросить те же самые 6 символов. Делается это так:
# Отбрасываем первые 6 символов
print('Winterfell'[6:]) # => 'fell'
Определения
- else – способ задать блок кода, который будет выполнен, если условие с
if
не удовлетворено.
57. Тернарный оператор
Python: Тернарный оператор
Вкратце
<expression on true> if <predicate> else <expression on false>
# например
number if number >= 0 else -number
Посмотрите на определение функции, которая возвращает модуль переданного числа:
def abs(number):
if number >= 0:
return number
return -number
Можно ли записать более лаконично? Что-то вроде return ОТВЕТ В ЗАВИСИМОСТИ ОТ УСЛОВИЯ
? Для этого справа от return
должно быть выражение, но if
— это инструкция, а не выражение.
В Python существует конструкция, которая по своему действию аналогична конструкции if-else, но при этом является выражением. Она называется тернарный оператор.
Тернарный оператор — единственный в своем роде оператор, требующий три операнда:
def abs(number):
return number if number >= 0 else -number
Общий паттерн выглядит так: <expression on true> if <predicate> else <expression on false>
.
Давайте перепишем начальный вариант get_type_of_sentence()
аналогично:
Было:
def get_type_of_sentence(sentence):
last_char = sentence[-1]
if last_char == '?':
return 'question'
return 'normal'
Стало:
def get_type_of_sentence(sentence):
last_char = sentence[-1]
return 'question' if last_char == '?' else 'normal'
print(get_type_of_sentence('Hodor')) # => normal
print(get_type_of_sentence('Hodor?')) # => question
Если вы помните в чём сила выражений, то вероятно уже догадались, что тернарный оператор можно вкладывать в тернарный оператор. Не делайте этого 🙂 Такой код тяжело и читать и отлаживать, это очень плохая практика.
Задание
Реализуйте функцию flip_flop()
, которая принимает на вход строку и, если эта строка равна 'flip'
, возвращает строку 'flop'
. В противном случае функция должна вернуть 'flip'
.
Примеры вызова:
print(flip_flop('flip')) # => 'flop'
print(flip_flop('flop')) # => 'flip'
Попробуйте написать два варианта функции: с обычным if-else, и с тернарным оператором.
Определения
- Тернарный оператор – способ превратить простую условную инструкцию в выражение, например,
number if number >= 0 else -number
.
58. else + if = elif
Python: else + if = elif
Вкратце
if условие 1:
# код, который будет выполнен,
# если условие 1 истинно
elif условие 2:
# код, который будет выполнен,
# если условие 1 ложно, но условие 2 истинно
elif условие 3:
# код, который будет выполнен,
# если условия 1 и 2 ложны, но условие 3 истинно
else:
# код, который будет выполнен
# в ином случае
Функция get_type_of_sentence()
из предыдущего урока различает только вопросительные и обычные предложения. Давайте попробуем добавить поддержку восклицательных предложений:
def get_type_of_sentence(sentence):
last_char = sentence[-1]
if last_char == '?':
sentence_type = 'question'
if last_char == '!':
sentence_type = 'exclamation'
else:
sentence_type = 'normal'
return 'Sentence is ' + sentence_type
print(get_type_of_sentence('Who?')) # => 'Sentence is normal'
print(get_type_of_sentence('No')) # => 'Sentence is normal'
print(get_type_of_sentence('No!')) # => 'Sentence is exclamation'
Мы добавили еще одну проверку (“exclamation” переводится «восклицание»). Технически функция работает, но вопросительные предложения трактует неверно, да и с точки зрения семантики есть проблемы.
- Проверка на наличие восклицательного знака происходит в любом случае, даже если уже был обнаружен вопросительный знак.
- Ветка
else
описана именно для второго условия, но не для первого (именно поэтому вопросительное предложение становится"normal"
).
Для исправления ситуации воспользуемся ещё одной возможностью условной конструкции:
def get_type_of_sentence(sentence):
last_char = sentence[-1]
if last_char == '?':
sentence_type = 'question'
elif last_char == '!':
sentence_type = 'exclamation'
else:
sentence_type = 'normal'
return 'Sentence is ' + sentence_type
print(get_type_of_sentence('Who?')) # => 'Sentence is question'
print(get_type_of_sentence('No')) # => 'Sentence is normal'
print(get_type_of_sentence('No!')) # => 'Sentence is exclamation'
Теперь все условия выстроены в единую конструкцию. elif
— это «если не выполнено предыдущее условие, но выполнено текущее». Получается такая схема:
- если последняя буква
?
, то'question'
- иначе, если последняя буква
!
, то'exclamation'
- иначе
'normal'
Выполнится только один из блоков кода, относящихся ко всей конструкции if
.
Задание
На электронной карте Вестероса, которую реализовал Сэм, союзники Старков отображены зелёным кружком, враги — красным, а нейтральные семьи — серым.
Напишите для Сэма функцию who_is_this_house_to_starks()
, которая принимает на вход фамилию семьи и возвращает одно из трёх значений: 'friend'
, 'enemy'
, 'neutral'
.
Правила определения:
- Друзья (
'friend'
):'Karstark'
,'Tully'
- Враги (
'enemy'
):'Lannister'
,'Frey'
- Любые другие семьи считаются нейтральными (
'neutral'
)
Примеры вызова:
print(who_is_this_house_to_starks('Karstark')) # => 'friend'
print(who_is_this_house_to_starks('Frey')) # => 'enemy'
print(who_is_this_house_to_starks('Joar')) # => 'neutral'
print(who_is_this_house_to_starks('Ivanov')) # => 'neutral'
Определения
- else + if = elif – способ задать несколько альтернативных условий.
59. Истинность выражений
Python: Истинность выражений
Программисту часто приходится в зависимости от значения выражения принимать какие-то решения. Мы уже знаем, что в условных операторах обычно используются предикаты или значения типа bool
. Так вот, в Python значения многих других типов могут быть истинны или ложны «по смыслу», т.е. могут выступать в роли предикатов!
Например, пустая строка, если её использовать в качестве условия, будет означать «ложь», а непустая — «истину»!
Пример:
def test_emptiness(string):
if string:
return 'non-empty'
return 'empty'
print(test_emptiness('foo')) # => 'non-empty'
print(test_emptiness('')) # => 'empty'
Число тоже «ложно», если равно нулю и «истинно», если отлично от нуля.
Пример:
def can_divide_by(number):
if number:
return 'Yes, you can!'
return "No, you can't"
print(can_divide_by(10)) # => 'Yes, you can!'
print(can_divide_by(0)) # => "No, you can't"
Чтобы узнать, «истинно» ли некое значение или «ложно», можно воспользоваться функцией bool()
. Достаточно передать этой функции значение любого типа!
print(bool(0)) # => False
print(bool(10)) # => True
print(bool('')) # => False
print(bool('a')) # => True
Задание
Напишите функцию is_falsy()
, которая проверяет, трактуется ли переданное значение, как ложное с точки зрения Python. Обратите внимание, само название функции должно уже вам говорить, что реализовать предстоит функцию-предикат (мы уже проходили, что это такое). А слово “проверяет” в данном случае означает, что is_falsy()
возвращает “истину”, если Python считает значение ложным (и наоборот).
В процессе решения вам может пригодиться упомянутая функция bool()
.
Советы
- Логический (булев) тип данных.
Циклы
Любой код может повторяться десятки, тысячи, миллионы раз. В комбинации с другими известными нам инструментами — переменными и условиями — это открывает множество возможностей по построению программ и сложных систем. Приведем простой пример. Вам нужно найти конкретную фразу в учебнике из 500 страниц. Фразу вы помните, а вот номер страницы нет. Самый простой (и долгий) способ — последовательно просматривать страницы до тех пор, пока не найдете нужную. Для выполнения таких повторяющихся действий и нужны циклы.
60. Цикл While
Python: Цикл While
Программы, которые мы пишем, становятся всё сложнее и объемнее. Они все ещё очень далеки от реальных программ, где количество строк кода измеряется десятками и сотнями тысяч (а иногда и миллионами), но текущая сложность уже способна заставить напрячься людей без опыта. Начиная с этого урока, мы переходим к одной из самых сложных базовых тем в программировании – циклам.
Любые прикладные программы служат очень прагматичным целям. Они помогают управлять сотрудниками, финансами, развлекают в конце концов. Несмотря на различия, все эти программы выполняют заложенные в них алгоритмы, которые очень похожи между собой. Что это такое? Алгоритм — это последовательность действий (инструкций), которая приводит нас к некоему ожидаемому результату. В принципе, это описание подходит под любую программу, но под алгоритмами обычно понимается что-то более специфичное.
Представьте себе, что у нас есть книга и мы хотим найти внутри неё какую-то конкретную фразу. Саму фразу мы помним, но не знаем, на какой она странице. Как найти нужную страницу? Самый простой (и долгий) способ — последовательно просматривать страницы до тех пор, пока мы не найдем нужную. В худшем случае придется просмотреть все страницы, но результат мы всё равно получим. Именно этот процесс и называется алгоритмом. Он включает в себя логические проверки (нашли ли фразу) и перебор страниц. Количество страниц, которое придется посмотреть, заранее не известно, но сам процесс просмотра повторяется из раза в раз совершенно одинаковым образом. Для выполнения повторяющихся действий как раз и нужны циклы. Каждый повтор, в таком случае, называется итерацией.
Напишем функцию с простым циклом, который будет n
раз выводить на экран строку 'Hello!'
:
def print_hello(n):
counter = 0
while counter < n:
print('Hello!')
counter = counter + 1
print_hello(2)
Hello! Hello!
Теперь подробно проанализируем пример функции с циклом, который выводит на экран числа от 1 до числа-аргумента:
print_numbers(3)
// => 1
// => 2
// => 3
Эту функцию невозможно реализовать уже изученными средствами, так как количество выводов на экран заранее не известно. А с циклами это не составит никаких проблем:
def print_numbers(last_number):
# i сокращение от index (порядковый номер)
# используется по общему соглашению во множестве языков
# как счетчик цикла
i = 1
while i <= last_number:
print(i)
i = i + 1
print('finished!')
print_numbers(3)
1 2 3 finished!
Цикл while
состоит из трёх элементов:
- Ключевое слово
while
. - Предикат. Условие, которое указывается после
while
. Это условие вычисляется на каждой итерации. - Блок кода (тело цикла).
Каждое выполнение тела называется итерацией. В нашем примере вызов print_numbers(3)
породил три итерации, на каждой из которых была выведена на экран переменная i
.
Конструкция читается так: «делать то, что указано в теле цикла пока истинно условие (предикат) i <= last_number
». Разберём работу этого кода для вызова print_numbers(3)
:
# Инициализируется i
i = 1
# Предикат возвращает true, поэтому выполняется тело цикла
while 1 <= 3
# print(1)
# i = 1 + 1
# Закончилось тело цикла, поэтому происходит возврат в начало
while 2 <= 3
# print(2)
# i = 2 + 1
# Закончилось тело цикла, поэтому происходит возврат в начало
while 3 <= 3
# print(3)
# i = 3 + 1
# Предикат возвращает false, поэтому выполнение переходит за цикл
while 4 <= 3
# print('finished!');
# На этом этапе i равен 4, но он нам уже не нужен
# функция завершается
Самое главное в цикле — завершение (выход). Процесс, который порождает цикл, должен в конце концов остановится. Ответственность за остановку полностью лежит на программисте.
Обычно задача сводится к введению переменной, называемой «счётчиком цикла». Сначала он инициализируется, то есть ему задаётся начальное значение. В нашем примере это строчка i = 1
. Затем в условии цикла проверяется, не достиг ли счётчик своего предельного значения.
В нашем примере предельное значение определяется аргументом функции. Если условие цикла не выполнено, то тело не выполняется и интерпретатор двигается дальше, выполняя инструкции после цикла. Но если условие цикла истинно, то выполняется тело, в котором находится ключевой элемент остановки — изменение счетчика. Обычно его делают в конце тела, и это изменение — одно из редких мест, где невозможно обойтись без переменной. В нашем примере за изменение отвечает строчка i = i + 1
.
На этом моменте новички делают больше всего ошибок. Например, случайно забытое увеличение счётчика или неправильная проверка в предикате способны привести к зацикливанию. Это ситуация, при которой цикл работает бесконечно и программа никогда не останавливается. В таком случае приходится её завершать принудительно (кто знает, может быть когда зависают реальные программы, в этот момент внутри них выполняется бесконечный цикл).
def print_numbers(last_number):
i = 1
# Этот цикл никогда не остановится
# и будет печатать всегда одно значение
while i <= last_number:
print(i)
print('finished!')
В некоторых случаях бесконечные циклы полезны. Здесь мы такие случаи не рассматриваем, но полезно видеть как выглядит этот код:
while True:
# Что-то делаем
Подводя итог. Когда всё же нужны циклы, а когда можно обойтись без них? Физически невозможно обойтись без циклов тогда, когда алгоритм решения задачи требует повторения каких-то действий, как в примере с книгой, и количество этих операций заранее неизвестно.
Задание
Модифицируйте функцию print_numbers()
, так, чтобы она выводила числа в обратном порядке. Для этого нужно идти от верхней границы к нижней. То есть, счетчик должен быть инициализирован максимальным значением, а в теле цикла его нужно уменьшать до нижней границы.
Пример вызова и вывода:
print_numbers(4)
4 3 2 1 finished!
Определения
- Цикл While – инструкция для повторения кода, пока удовлетворяется какое-то условие.
61. Агрегация данных (Числа)
Python: Агрегация данных (Числа)
Отдельный класс задач, который не может обойтись без циклов, называется агрегированием данных. К таким задачам относятся поиск максимального, минимального, суммы, среднего арифметического и т.п. Их главная особенность в том, что результат зависит от всего набора данных. Для рассчёта суммы нужно сложить все числа, для вычисления максимального нужно сравнить все числа.
С такими задачами хорошо знакомы все, кто занимаются числами, например бухгалтеры или маркетологи. Обычно их выполняют в таблицах наподобие Microsoft Excel или Google Tables.
Разберём самый простой пример – поиск суммы набора чисел. Реализуем функцию, которая складывает числа в указанном диапазоне, включая границы. Диапазоном в данном случае называется ряд чисел от какого-то начала до определенного конца. Например, диапазон [1, 10] включает в себя все целые числа от 1 до 10.
sum_numbers_from_range(5, 7) # 5 + 6 + 7 = 18
sum_numbers_from_range(1, 2) # 1 + 2 = 3
# [1, 1] диапазон с одинаковым началом и концом – тоже диапазон
# он в себя включает ровно одно число – саму границу диапазона
sum_numbers_from_range(1, 1) # 1
sum_numbers_from_range(100, 100) # 100
Для реализации этого кода нам понадобится цикл, так как сложение чисел – это итеративный процесс (он повторяется для каждого числа), а количество итераций зависит от размера диапазона. Перед тем, как смотреть код, попробуйте ответьте на вопросы ниже:
- Каким значением инициализировать счетчик?
- Как он будет изменяться?
- Когда цикл должен остановиться?
Попробуйте сначала подумать над этими вопросами, а затем посмотрите код ниже:
def sum_numbers_from_range(start, finish):
# Технически можно менять start
# Но входные аргументы нужно оставлять в исходном значении
# Это сделает код проще для анализа
i = start
sum = 0 # Инициализация суммы
while i <= finish: # Двигаемся до конца диапазона
sum = sum + i # Считаем сумму для каждого числа
i = i + 1 # Переходим к следующему числу в диапазоне
# Возвращаем получившийся результат
return sum
Общая структура цикла здесь стандартна. Есть счетчик, который инициализируется начальным значением диапазона, есть сам цикл с условием остановки при достижении конца диапазона, и, наконец, изменение счетчика в конце тела цикла. Количество итераций в таком цикле равно finish - start + 1
. То есть для диапазона от 5 до 7 – это 7 – 5 + 1, то есть 3 итерации.
Главные отличия от обычной обработки связаны с логикой вычислений результата. В задачах на агрегацию всегда есть какая-то переменная, которая хранит внутри себя результат работы цикла. В коде выше это sum
. На каждой итерации цикла происходит её изменение, прибавление следующего числа в диапазоне: sum = sum + i
. Весь процесс выглядит так:
# Для вызова sum_numbers_from_range(2, 5)
sum = 0
sum = sum + 2 # 2
sum = sum + 3 # 5
sum = sum + 4 # 9
sum = sum + 5 # 14
# 14 – результат сложения чисел в диапазоне [2, 5]
У переменной sum
есть начальное значение, равное 0. Зачем вообще задавать значение? Любая повторяющаяся операция начинается с какого-то значения. В математике существует понятие нейтральный элемент операции (у каждой операции свой элемент). Это понятие имеет очень простой смысл. Операция с этим элементом не изменяет то значение, над которым проводится операция. В сложении любое число плюс ноль дает само число. При вычитании – тоже самое. Даже у конкатенации есть нейтральный элемент – это пустая строка: '' + 'one'
будет ‘one’.
Вопрос на самопроверку. Какой нейтральный элемент у операции умножения?
Задание
Реализуйте функцию multiply_number_from_range()
, которая перемножает числа в указанном диапазоне включая границы диапазона. Пример вызова:
multiply_number_from_range(1, 5) # 1 * 2 * 3 * 4 * 5 = 120
multiply_number_from_range(2, 3) # 2 * 3 = 6
multiply_number_from_range(6, 6) # 6
62. Агрегация данных (Строки)
Python: Агрегация данных (Строки)
Агрегация применяется не только к числам, но и к строкам. Это такие задачи, в которых строка формируется динамически, то есть заранее неизвестно, какого она размера и что будет содержать.
Представьте себе функцию, которая умеет «умножать» строку, то есть она повторяет её указанное количество раз:
repeat('hexlet', 3) # 'hexlethexlethexlet'
Принцип работы этой функции довольно простой: в цикле происходит «наращивание» строки указанное количество раз:
def repeat(text, times):
# Нейтральный элемент для строк – пустая строка
result = ''
i = 1
while i <= times:
# Каждый раз добавляем строку к результату
result = result + text
i = i + 1
return result
Распишем выполнение этого кода по шагам:
# Для вызова repeat('hexlet', 3)
result = ''
result = result + 'hexlet' # hexlet
result = result + 'hexlet' # hexlethexlet
result = result + 'hexlet' # hexlethexlethexlet
Задание
Реализуйте функцию join_numbers_from_range()
, которая объединяет все числа из диапазона в строку:
join_numbers_from_range(1, 1) # '1'
join_numbers_from_range(2, 3) # '23'
join_numbers_from_range(5, 10) # '5678910'
63. Обход строк
Python: Обход строк
Циклы подходят не только для обработки чисел, но и при работе со строками. В первую очередь благодаря возможности получить конкретный символ по его индексу. Ниже пример кода, который распечатывает буквы каждого слова на отдельной строке:
def print_name_by_symbol(name):
i = 0
# Такая проверка будет выполняться до конца строки
# включая последний символ. Его индекс `length - 1`.
while i < len(name):
# Обращаемся к символу по индексу
print(name[i])
i = i + 1
name = 'Arya'
print_name_by_symbol(name)
# => 'A'
# => 'r'
# => 'y'
# => 'a'
Самое главное в этом коде, поставить правильное условие в while
. Это можно сделать сразу двумя способами: i < len(name)
или i <= len(name) - 1
. Оба способа приводят к одному результату.
Задание
Реализуйте функцию print_name_by_symbol()
, которая печатает переданное слово посимвольно, как в примере из теории, но делает это в обратном порядке.
name = 'Arya'
print_name_by_symbol(name)
# => 'a'
# => 'y'
# => 'r'
# => 'A'
64. Условия внутри тела цикла
Python: Условия внутри тела цикла
Тело цикла, как и тело функции — это место выполнения инструкций. Значит, мы можем использовать внутри него всё изученное ранее, например — условные конструкции.
Представьте себе функцию, которая считает, сколько раз входит буква в предложение. Пример её работы:
count_chars('Fear cuts deeper than swords.', 'e') # 4
# Если вы ничего не нашли, то результат — 0 совпадений
count_chars('Sansa', 'y') # 0
Перед тем как посмотреть её содержимое, попробуйте ответить на вопросы:
- Является ли эта операция агрегацией?
- Какой будет проверка на вхождение символа?
def count_chars(string, char):
index = 0
count = 0
while index < len(string):
if string[index] == char:
# Считаем только подходящие символы
count = count + 1
# Счётчик увеличивается в любом случае
index = index + 1
return count
Эта задача является агрегирующей. Несмотря на то, что она считает не все символы, для подсчёта самой суммы все равно приходится анализировать каждый символ.
Ключевое отличие этого цикла от рассмотренных в наличии условия внутри тела. Переменная count
увеличивается только в том случае, когда текущий рассматриваемый символ совпадает с ожидаемым.
В остальном — это типичная агрегатная функция, которая возвращает количество нужных символов вызываемому коду.
Задание
Функция из теории учитывает регистр букв. То есть A
и a
с её точки зрения разные символы. Реализуйте вариант этой же функции, так чтобы регистр букв был не важен:
count_chars('HexlEt', 'e') # 2
count_chars('HexlEt', 'E') # 2
65. Формирование строк в циклах
Python: Формирование строк в циклах
Ещё одно использование циклов – формирование строк. Подобная задача нередко встречается в веб-программировании. Она сводится к обычной агрегации с применением интерполяции или конкатенации.
Переворот строки (запись её задом-наперёд) — простейшая алгоритмическая задача, которую иногда задают на собеседованиях. Правильный способ перевернуть строку — использовать функцию из стандартной библиотеки. Но в целях обучения полезно реализовать её самостоятельно.
Один из алгоритмов выглядит так: строим новую строку, перебирая символы исходной строки в обратном порядке.
def reverse_string(string):
index = len(string) - 1
reversed_string = ''
while index >= 0:
current_char = string[index]
reversed_string = reversed_string + current_char
# То же самое через интерполяцию
# reversed_string = "{}{}".format(reversed_string, current_char)
index = index - 1
return reversed_string
reverse_string('Game Of Thrones') # 'senorhT fO emaG'
# Проверка нейтрального элемента
reverse_string('') # ''
Разберём функцию построчно:
index = len(string) - 1
— записываем в новую переменную индекс последнего символа строки (напомним, что индексы начинаются с нуля).reversed_string = ''
— инициализируем строку, куда будем записывать результат.while index >= 0:
— условие: повторяем тело цикла, пока текущий индекс не дошёл до0
, то есть до первого символа.current_char = string[index]
— берём из строки символ по текущему индексу.reversed_string = reversed_string + current_char
— записываем в строку-результат новое значение: текущая строка-результат + новый символ.index = index - 1
— обновляем счётчикreturn reversed_string
— когда цикл завершился, возвращаем строку-результат.
Обязательно скопируйте эту функцию в https://repl.it/languages/python3 и поэкспериментируйте с ней.
Работая со строками, программисты часто допускают ошибку «выход за границы строки». При неправильном подборе начального значения счётчика или ошибке в предикате цикла может получиться ситуация, при которой идёт обращение к несуществующему символу.
Особенно часто забывают о том, что индекс последнего элемента всегда меньше на единицу размера строки. В строках начальный индекс равен 0
, а значит индекс последнего элемента — len(str) - 1
(длина минус 1).
Задание
Реализуйте функцию my_substr()
, которая извлекает из строки подстроку указанной длины. Она принимает на вход два аргумента: строку и длину, и возвращает подстроку, начиная с первого символа:
Пример вызова:
string = 'If I look back I am lost'
print(my_substr(string, 1)) # => 'I'
print(my_substr(string, 7)) # => 'If I lo'
Используйте тот же подход, что в функции для переворота строки из урока: собирайте строку-результат в цикле, перебирая начальную строку до определённого момента.
66. Пограничные случаи
Python: Пограничные случаи
Функция my_substr()
, которую вы реализовали в прошлом уроке, содержит множество ошибок. «Но ведь она прошла проверки!». Да, но в этих проверках не было так называемых пограничных случаев. Функция нормально работала с нормальными аргументами, но как она поведёт себя, если передать ей такие варианты длины?
0
.- Отрицательное число.
- Число, превышающее реальный размер строки.
Функция my_substr()
не рассчитана на такие варианты. Можно подумать, что это не проблема: функция работает в нормальных условиях, и просто не нужно передавать ей «плохие» аргументы. В идеальном мире — да, но в реальном мире ваш код будет запускаться в разных ситуациях, с разными комбинациями условий и данных. Нельзя быть уверенным, что аргументы всегда будут корректными, поэтому нужно учитывать все случаи, в рамках здравого смысла.
Ошибки в пограничных случаях — самая частая причина логических ошибок в программах. Программисты всегда забывают что-нибудь учесть. Такие ошибки часто проявляются не сразу и могут долгое время не приводить к видимым проблемам. Программа продолжает работать, но в какой-то момент обнаруживается, что в результатах есть ошибки. Часто причина в динамической типизации Python.
Умение справляться с такими ошибками приходит с опытом, через постоянные косяки в стиле «ой, забыл проверить на пустую строку!».
Давайте представим себе расширенную функцию my_substr()
. Она принимает три аргумента: строку, индекс и длину извлекаемой подстроки. Функция возвращает подстроку указанной длины, начиная с указанного индекса. Примеры вызова:
string = 'If I look back I am lost'
print(my_substr(string, 0, 1)) # => 'I'
print(my_substr(string, 3, 6)) # => 'I look'
Прикинем, что может пойти не так. Какие пограничные случаи стоит учитывать:
- Отрицательная длина извлекаемой подстроки.
- Отрицательный заданный индекс.
- Заданный индекс выходит за границу всей строки.
- Длина подстроки в сумме с заданным индексом выходит за границу всей строки.
В реализации функции каждый пограничный случай будет отдельным куском кода, скорее всего реализованным с помощью if
.
Чтобы написать функцию my_substr()
и защититься от этих случаев, стоит реализовать отдельную функцию, которая будет проверять аргументы на корректность. Займёмся этим в задании.
Задание
Реализуйте функцию-предикат is_arguments_for_substr_correct()
, которая принимает три аргумента:
- строку;
- индекс, с которого начинать извлечение;
- длину извлекаемой подстроки.
Функция возвращает False
, если хотя бы одно из условий истинно:
- Отрицательная длина извлекаемой подстроки.
- Отрицательный заданный индекс.
- Заданный индекс выходит за границу всей строки.
- Длина подстроки в сумме с заданным индексом выходит за границу всей строки.
В ином случае функция возвращает True
.
Не забывайте, что индексы начинаются с 0
, поэтому индекс последнего элемента — это «длина строки минус 1».
Пример вызова:
string = 'Sansa Stark'
print(is_arguments_for_substr_correct(string, -1, 3)) # => False
print(is_arguments_for_substr_correct(string, 4, 100)) # => False
print(is_arguments_for_substr_correct(string, 10, 10)) # => False
print(is_arguments_for_substr_correct(string, 11, 1)) # => False
print(is_arguments_for_substr_correct(string, 3, 3)) # => True
print(is_arguments_for_substr_correct(string, 0, 5)) # => True
67. Синтаксический сахар
Python: Синтаксический сахар
Подобные конструкции index = index + 1
в Python используются довольно часто, поэтому создатели языка добавили сокращённый вариант записи: index += 1
.
Важно понимать, что отличия исключительно в способе записи. Интерпретатор превращает сокращённую конструкцию в развёрнутую.
Такие сокращения принято называть синтаксическим сахаром, потому что они делают процесс написания кода немного проще и приятнее, «подслащивая» его 🙂
Существуют сокращённые формы для всех арифметических операций и для конкатенации строк:
a = a + 1
→a += 1
a = a - 1
→a -= 1
a = a * 2
→a *= 2
a = a / 1
→a /= 1
Задание
Реализуйте функцию filter_string()
, принимающую на вход строку и символ, и возвращающую новую строку, в которой удален переданный символ во всех его позициях.
Пример вызова:
text = 'If I look back I am lost'
filter_string(text, 'I') # 'f look back am lost'
filter_string(text, 'o') # 'If I lk back I am lst'
68. Возврат из циклов
Python: Возврат из циклов
Работа с циклами обычно сводится к двум сценариям:
- Агрегация. Накопление результата во время итераций и работа с ним после цикла. Переворот строки как раз относится к такому варианту.
- Выполнение цикла до достижения необходимого результата и выход. Например, задача поиска простых чисел. Напомним, что простое число — это число, которое делится без остатка только на себя и на единицу.
Рассмотрим простой алгоритм проверки простоты числа. Будем делить искомое число x
на все числа из диапазона от двух до x - 1
и смотреть остаток. Если в этом диапазоне не найден делитель, который делит число x
без остатка, значит перед нами простое число.
Если задуматься, то можно заметить, что достаточно проверять числа не до x - 1
, а до половины числа. Например, 11 не делится на 2, 3, 4, 5. Но и дальше гарантированно не будет делиться на числа больше своей половины. Значит, можно провести небольшую оптимизацию и проверять деление только до x / 2
.
def is_prime(number):
if number < 2:
return False
divider = 2
while divider <= number / 2:
if number % divider == 0:
return False
divider += 1
return True
print(is_prime(1)) # => False
print(is_prime(2)) # => True
print(is_prime(3)) # => True
print(is_prime(4)) # => False
Алгоритм построен таким образом, что если во время последовательного деления на числа до x / 2
находится хоть одно, которое делит без остатка, то переданный аргумент — не простое число, а значит дальнейшие вычисления не имеют смысла. В этом месте стоит возврат False
.
И только если цикл отработал целиком, можно сделать вывод, что число — простое, так как не было найдено ни одного числа, которое делит число без остатка.
Задание
Реализуйте функцию does_contain()
, которая проверяет с учётом регистра, содержит ли строка указанную букву (в этот раз не пользуйтесь оператором in
!). Функция принимает два параметра:
- Строка
- Буква для поиска
Пример вызова:
print(does_contain('Renly', 'R')) # => True
print(does_contain('Renly', 'r')) # => False
print(does_contain('Tommy', 'm')) # => True
print(does_contain('Tommy', 'd')) # => False
Советы
- Список простых чисел
69. Цикл For
Python: Цикл For
Цикл while
– это основной цикл в Python, но не единственный.
Существует также цикл for
. Он перебирает все элементы объекта, который ему передали. Циклу for
не нужен счётчик. Он сам знает, когда необходимо остановиться и закончить работу. Такая магия возможна, благодаря особому устройству объектов, по которым совершается итерация. Подробно эта тема рассматривается в наших курсах на Хекслете. Сейчас достаточно знать, что строка является таким объектом и подходит для обработки циклом for
.
Посмотрим реализацию переворота строки через цикл for
:
def reverse(string):
result = ''
for char in string:
result = char + result
return result
reverse('go!') # '!og'
В переменную char
кладётся очередное значение на каждом шаге итерации. Имя этой переменной может быть любым.
Можно читать так: для каждого символа строки повторить операцию конкатенации.
В определении цикла for
нет ни счётчика ни условия выхода из него. Он не знает заранее, какое количество итераций будет совершено.
Задание
В одном из предыдущих уроков мы уже написали функцию filter_string()
. Напомним, она принимает на вход строку и символ и возвращает новую строку, в которой удалён переданный символ во всех его позициях. На этот раз реализуйте эту функцию с помощью цикла for
. Дополнительное условие: регистр исключаемого символа не имеет значения.
Пример вызова:
text = 'If I look forward I am win'
filter_string(text, 'i') # 'f look forward am wn'
filter_string(text, 'O') # 'If I lk frward I am win'
В этом руководстве вы узнаете об операторах в Python, о важности использования отступов и комментариев.
Инструкции
Инструкции, которые может выполнять интерпретатор Python, называются операторами. Например, a = 1
— это оператор присваивания. Оператор if
, оператор for
, оператор while
и т. д. — это другие типы операторов, которые мы обсудим позже.
Многострочные инструкции
В Python для окончания инструкции используют символ новой строки. Но можно разбить инструкцию на несколько строк символом продолжения строки (\). Например:
a = 1 + 2 + 3 + \
4 + 5 + 6 + \
7 + 8 + 9
Это явное продолжение строки. В Python продолжение строки выполняется внутри круглых ( )
, квадратных [ ]
и фигурных скобок { }
. Например, так мы можем реализовать показанную выше многострочность:
a = (1 + 2 + 3 +
4 + 5 + 6 +
7 + 8 + 9)
Здесь круглые скобки ( )
неявно продолжают строку. То же самое с [ ]
и { }
. Например:
colors = ['красный',
'голубой',
'зеленый']
Мы также можем поместить несколько выражений в одну строку с помощью точки с запятой, как показано ниже:
a = 1; b = 2; c = 3
Отступы
Большинство языков программирования, таких как C, C ++ и Java, используют фигурные скобки { }
для определения блока кода. Однако Python использует отступы.
Блок кода (тело функции, цикла и т. д.) начинается с отступа и заканчивается первой строкой без отступа. Размер отступа зависит от вас, но он должен быть одинаковым на протяжении всего блока.
Как правило, для отступов используют четыре пробела или табуляцию. Вот пример:
for i in range(1,11):
print(i)
if i == 5:
break
Применение отступов в Python делает код аккуратным и чистым. Программы благодаря отступам выглядят последовательными. С ними блоки кода визуально легче отличать друг от друга.
Сравните:
if True:
print('Привет')
a = 5
и
if True: print('Привет'); a = 5
Оба варианта допустимы и сделают одно и то же, но первый вариант — читабельнее.
Неправильный отступ приведет к ошибке IndentationError
.
Комментарии
Комментарии очень важны при написании программы. Они описывают, что происходит внутри программы, чтобы человеку, который читает код, было проще разобраться в нем.
Вы можете забыть детали программы, написанной даже месяц назад. Поэтому всегда полезно уделять время на пояснение основных моментов в комментариях.
В Python для комментариев используется символ решетки (#).
Комментарий заканчивается символом новой строки. Интерпретатор Python игнорирует комментарии.
#Это комментарий
#выводим «Привет»
print('Привет')
Многострочные комментарии
В Python можно использовать многострочные комментарии. Один из способов — использовать символ решетки (#) в начале каждой строки. Например:
#Это длинный комментарий,
#и он продолжается
#на несколько строк
Можно поступить иначе — использовать тройные кавычки: '''
или """
.
Тройные кавычки обычно используются для многострочных строк. Но их также можно использовать для многострочных комментариев. Если они не являются строками документации, они не создают никакого дополнительного кода.
"""Это тоже
пример прекрасного
многострочного комментария"""
Строки документации (docstrings) в Python
Docstring — это сокращение от documentation string (строка документации).
Строки документации в Python — это строковые литералы, которые появляются сразу после определения функции, метода, класса или модуля.
При написании строк документации используются тройные кавычки. Например:
def double(num):
"""Функция, которая удваивает значение"""
return 2*num
Строки документации появляются сразу после определения функции, класса или модуля. Это и отличает их от многострочных комментариев, написанных с использованием тройных кавычек.
Строка документации связана с объектом как его атрибут __doc__
.
Так что мы можем получить доступ к строкам документации функции с помощью следующих строк кода:
def double(num):
"""Функция, которая удваивает значение"""
return 2*num
print(double.__doc__)
Вывод:
Функция, которая удваивает значение
Pavel556
Как инструкции отделяются друг от друга в Python?
(нужно выбрать все корректные ответы)
1)С помощью запятой
2)С помощью точки с запятой
3)Каждая инструкция пишется на отдельной строке
4)Никак интерпретатор сам разбивает код на команды
Ни один из ответов не принимается. Даже 3-й
Только начал курс у вас и сразу столкнулся с такой кривотой))
Timofey Sobolev
Pavel556, В соответствии с заданием нужно выбрать все корректные ответы. Тут их два:
- С помощью точки с запятой
- Каждая инструкция пишется на отдельной строке
user-a88e2ebd776299fc
Timofey Sobolev, также с помощью запятой(но тут это считается ошибкой)
проверьте если интересно
print(‘Mother of Dragons.’), print(‘Drakarys!’)
print(‘Mother of Dragons.’); print(‘Drakarys!’)
print(‘Mother of Dragons.’)
print(‘Drakarys!’)
результат