├── .gitbook.yaml ├── .gitignore ├── CONTRIBUTING.md ├── LICENSE.md ├── README.md ├── SUMMARY.md ├── _static └── cover.png ├── book ├── __slots__magic.md ├── args_and_kwargs.md ├── classes.md ├── collections.md ├── comprehensions.md ├── context_managers.md ├── coroutines.md ├── debugging.md ├── decorators.md ├── enumerate.md ├── exceptions.md ├── for_-_else.md ├── function_caching.md ├── generators.md ├── global_&_return.md ├── lambdas.md ├── map_filter.md ├── mutation.md ├── object_introspection.md ├── one_liners.md ├── open_function.md ├── python_c_extension.md ├── set_-_data_structure.md ├── targeting_python_2_3.md ├── ternary_operators.md └── virtual_environment.md ├── cover.jpg └── cover_small.jpg /.gitbook.yaml: -------------------------------------------------------------------------------- 1 | root: ./ 2 | 3 | structure: 4 | readme: README.md 5 | summary: SUMMARY.md -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # IDE Settings 2 | .idea/ 3 | 4 | # emacs 5 | \#*\# 6 | .\#* 7 | *~ 8 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Помощь в развитии книги 2 | 3 | Любая помощь горячо приветствуется, в частности: 4 | 5 | - Исправления ошибок 6 | - Добавление новых глав 7 | 8 | Вы можете вносить свою лепту как на русском, так и на английском языках. 9 | В первом случае для вас открыты [issue tracker][4] и [pull requests][5] 10 | репозитория с переводом. Во втором - открывайте [pull request][1] или 11 | [репортите issue][2] в [оригинальный репозиторий][2] книги. Исправления, 12 | попадающие в английскую версию со временем будут переведены и попадут в 13 | русскую версию. Вы можете ускорить этот процесс при желании, самостоятельно 14 | переведя свои изменения на оба языка. Процесс миграции исправлений в обратном 15 | направлении в настоящий момент не предусмотрен. 16 | 17 | ## Контакты 18 | 19 | С автором - Muhammad Yasoob Ullah Khalid - можно связаться (на английском 20 | языке разумеется) через: 21 | 22 | - [Электронную почту](mailto:yasoob.khld@gmail.com) 23 | - [Issue tracker][2] оригинального репозитория книги 24 | 25 | С переводчиком - Павел Каратеев: 26 | 27 | - [Электронную почту](mailto:karateev.pavel@ya.ru) 28 | - Skype: karateev.pavel 29 | - ICQ: 676-963-191 30 | - [Issue tracker][4] русской версии книги 31 | 32 | [1]: https://github.com/yasoob/intermediatePython/pull/new/master 33 | [2]: https://github.com/yasoob/intermediatePython/issues/new 34 | [3]: https://github.com/yasoob/intermediatePython 35 | [4]: https://github.com/lancelote/interpy-ru/issues/new 36 | [5]: https://github.com/lancelote/interpy-ru/pull/new/master 37 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Attribution-NonCommercial-ShareAlike 4.0 International 2 | 3 | ======================================================================= 4 | 5 | Creative Commons Corporation ("Creative Commons") is not a law firm and 6 | does not provide legal services or legal advice. Distribution of 7 | Creative Commons public licenses does not create a lawyer-client or 8 | other relationship. Creative Commons makes its licenses and related 9 | information available on an "as-is" basis. Creative Commons gives no 10 | warranties regarding its licenses, any material licensed under their 11 | terms and conditions, or any related information. Creative Commons 12 | disclaims all liability for damages resulting from their use to the 13 | fullest extent possible. 14 | 15 | Using Creative Commons Public Licenses 16 | 17 | Creative Commons public licenses provide a standard set of terms and 18 | conditions that creators and other rights holders may use to share 19 | original works of authorship and other material subject to copyright 20 | and certain other rights specified in the public license below. The 21 | following considerations are for informational purposes only, are not 22 | exhaustive, and do not form part of our licenses. 23 | 24 | Considerations for licensors: Our public licenses are 25 | intended for use by those authorized to give the public 26 | permission to use material in ways otherwise restricted by 27 | copyright and certain other rights. Our licenses are 28 | irrevocable. Licensors should read and understand the terms 29 | and conditions of the license they choose before applying it. 30 | Licensors should also secure all rights necessary before 31 | applying our licenses so that the public can reuse the 32 | material as expected. Licensors should clearly mark any 33 | material not subject to the license. This includes other CC- 34 | licensed material, or material used under an exception or 35 | limitation to copyright. More considerations for licensors: 36 | wiki.creativecommons.org/Considerations_for_licensors 37 | 38 | Considerations for the public: By using one of our public 39 | licenses, a licensor grants the public permission to use the 40 | licensed material under specified terms and conditions. If 41 | the licensor's permission is not necessary for any reason--for 42 | example, because of any applicable exception or limitation to 43 | copyright--then that use is not regulated by the license. Our 44 | licenses grant only permissions under copyright and certain 45 | other rights that a licensor has authority to grant. Use of 46 | the licensed material may still be restricted for other 47 | reasons, including because others have copyright or other 48 | rights in the material. A licensor may make special requests, 49 | such as asking that all changes be marked or described. 50 | Although not required by our licenses, you are encouraged to 51 | respect those requests where reasonable. More_considerations 52 | for the public: 53 | wiki.creativecommons.org/Considerations_for_licensees 54 | 55 | ======================================================================= 56 | 57 | Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International 58 | Public License 59 | 60 | By exercising the Licensed Rights (defined below), You accept and agree 61 | to be bound by the terms and conditions of this Creative Commons 62 | Attribution-NonCommercial-ShareAlike 4.0 International Public License 63 | ("Public License"). To the extent this Public License may be 64 | interpreted as a contract, You are granted the Licensed Rights in 65 | consideration of Your acceptance of these terms and conditions, and the 66 | Licensor grants You such rights in consideration of benefits the 67 | Licensor receives from making the Licensed Material available under 68 | these terms and conditions. 69 | 70 | 71 | Section 1 -- Definitions. 72 | 73 | a. Adapted Material means material subject to Copyright and Similar 74 | Rights that is derived from or based upon the Licensed Material 75 | and in which the Licensed Material is translated, altered, 76 | arranged, transformed, or otherwise modified in a manner requiring 77 | permission under the Copyright and Similar Rights held by the 78 | Licensor. For purposes of this Public License, where the Licensed 79 | Material is a musical work, performance, or sound recording, 80 | Adapted Material is always produced where the Licensed Material is 81 | synched in timed relation with a moving image. 82 | 83 | b. Adapter's License means the license You apply to Your Copyright 84 | and Similar Rights in Your contributions to Adapted Material in 85 | accordance with the terms and conditions of this Public License. 86 | 87 | c. BY-NC-SA Compatible License means a license listed at 88 | creativecommons.org/compatiblelicenses, approved by Creative 89 | Commons as essentially the equivalent of this Public License. 90 | 91 | d. Copyright and Similar Rights means copyright and/or similar rights 92 | closely related to copyright including, without limitation, 93 | performance, broadcast, sound recording, and Sui Generis Database 94 | Rights, without regard to how the rights are labeled or 95 | categorized. For purposes of this Public License, the rights 96 | specified in Section 2(b)(1)-(2) are not Copyright and Similar 97 | Rights. 98 | 99 | e. Effective Technological Measures means those measures that, in the 100 | absence of proper authority, may not be circumvented under laws 101 | fulfilling obligations under Article 11 of the WIPO Copyright 102 | Treaty adopted on December 20, 1996, and/or similar international 103 | agreements. 104 | 105 | f. Exceptions and Limitations means fair use, fair dealing, and/or 106 | any other exception or limitation to Copyright and Similar Rights 107 | that applies to Your use of the Licensed Material. 108 | 109 | g. License Elements means the license attributes listed in the name 110 | of a Creative Commons Public License. The License Elements of this 111 | Public License are Attribution, NonCommercial, and ShareAlike. 112 | 113 | h. Licensed Material means the artistic or literary work, database, 114 | or other material to which the Licensor applied this Public 115 | License. 116 | 117 | i. Licensed Rights means the rights granted to You subject to the 118 | terms and conditions of this Public License, which are limited to 119 | all Copyright and Similar Rights that apply to Your use of the 120 | Licensed Material and that the Licensor has authority to license. 121 | 122 | j. Licensor means the individual(s) or entity(ies) granting rights 123 | under this Public License. 124 | 125 | k. NonCommercial means not primarily intended for or directed towards 126 | commercial advantage or monetary compensation. For purposes of 127 | this Public License, the exchange of the Licensed Material for 128 | other material subject to Copyright and Similar Rights by digital 129 | file-sharing or similar means is NonCommercial provided there is 130 | no payment of monetary compensation in connection with the 131 | exchange. 132 | 133 | l. Share means to provide material to the public by any means or 134 | process that requires permission under the Licensed Rights, such 135 | as reproduction, public display, public performance, distribution, 136 | dissemination, communication, or importation, and to make material 137 | available to the public including in ways that members of the 138 | public may access the material from a place and at a time 139 | individually chosen by them. 140 | 141 | m. Sui Generis Database Rights means rights other than copyright 142 | resulting from Directive 96/9/EC of the European Parliament and of 143 | the Council of 11 March 1996 on the legal protection of databases, 144 | as amended and/or succeeded, as well as other essentially 145 | equivalent rights anywhere in the world. 146 | 147 | n. You means the individual or entity exercising the Licensed Rights 148 | under this Public License. Your has a corresponding meaning. 149 | 150 | 151 | Section 2 -- Scope. 152 | 153 | a. License grant. 154 | 155 | 1. Subject to the terms and conditions of this Public License, 156 | the Licensor hereby grants You a worldwide, royalty-free, 157 | non-sublicensable, non-exclusive, irrevocable license to 158 | exercise the Licensed Rights in the Licensed Material to: 159 | 160 | a. reproduce and Share the Licensed Material, in whole or 161 | in part, for NonCommercial purposes only; and 162 | 163 | b. produce, reproduce, and Share Adapted Material for 164 | NonCommercial purposes only. 165 | 166 | 2. Exceptions and Limitations. For the avoidance of doubt, where 167 | Exceptions and Limitations apply to Your use, this Public 168 | License does not apply, and You do not need to comply with 169 | its terms and conditions. 170 | 171 | 3. Term. The term of this Public License is specified in Section 172 | 6(a). 173 | 174 | 4. Media and formats; technical modifications allowed. The 175 | Licensor authorizes You to exercise the Licensed Rights in 176 | all media and formats whether now known or hereafter created, 177 | and to make technical modifications necessary to do so. The 178 | Licensor waives and/or agrees not to assert any right or 179 | authority to forbid You from making technical modifications 180 | necessary to exercise the Licensed Rights, including 181 | technical modifications necessary to circumvent Effective 182 | Technological Measures. For purposes of this Public License, 183 | simply making modifications authorized by this Section 2(a) 184 | (4) never produces Adapted Material. 185 | 186 | 5. Downstream recipients. 187 | 188 | a. Offer from the Licensor -- Licensed Material. Every 189 | recipient of the Licensed Material automatically 190 | receives an offer from the Licensor to exercise the 191 | Licensed Rights under the terms and conditions of this 192 | Public License. 193 | 194 | b. Additional offer from the Licensor -- Adapted Material. 195 | Every recipient of Adapted Material from You 196 | automatically receives an offer from the Licensor to 197 | exercise the Licensed Rights in the Adapted Material 198 | under the conditions of the Adapter's License You apply. 199 | 200 | c. No downstream restrictions. You may not offer or impose 201 | any additional or different terms or conditions on, or 202 | apply any Effective Technological Measures to, the 203 | Licensed Material if doing so restricts exercise of the 204 | Licensed Rights by any recipient of the Licensed 205 | Material. 206 | 207 | 6. No endorsement. Nothing in this Public License constitutes or 208 | may be construed as permission to assert or imply that You 209 | are, or that Your use of the Licensed Material is, connected 210 | with, or sponsored, endorsed, or granted official status by, 211 | the Licensor or others designated to receive attribution as 212 | provided in Section 3(a)(1)(A)(i). 213 | 214 | b. Other rights. 215 | 216 | 1. Moral rights, such as the right of integrity, are not 217 | licensed under this Public License, nor are publicity, 218 | privacy, and/or other similar personality rights; however, to 219 | the extent possible, the Licensor waives and/or agrees not to 220 | assert any such rights held by the Licensor to the limited 221 | extent necessary to allow You to exercise the Licensed 222 | Rights, but not otherwise. 223 | 224 | 2. Patent and trademark rights are not licensed under this 225 | Public License. 226 | 227 | 3. To the extent possible, the Licensor waives any right to 228 | collect royalties from You for the exercise of the Licensed 229 | Rights, whether directly or through a collecting society 230 | under any voluntary or waivable statutory or compulsory 231 | licensing scheme. In all other cases the Licensor expressly 232 | reserves any right to collect such royalties, including when 233 | the Licensed Material is used other than for NonCommercial 234 | purposes. 235 | 236 | 237 | Section 3 -- License Conditions. 238 | 239 | Your exercise of the Licensed Rights is expressly made subject to the 240 | following conditions. 241 | 242 | a. Attribution. 243 | 244 | 1. If You Share the Licensed Material (including in modified 245 | form), You must: 246 | 247 | a. retain the following if it is supplied by the Licensor 248 | with the Licensed Material: 249 | 250 | i. identification of the creator(s) of the Licensed 251 | Material and any others designated to receive 252 | attribution, in any reasonable manner requested by 253 | the Licensor (including by pseudonym if 254 | designated); 255 | 256 | ii. a copyright notice; 257 | 258 | iii. a notice that refers to this Public License; 259 | 260 | iv. a notice that refers to the disclaimer of 261 | warranties; 262 | 263 | v. a URI or hyperlink to the Licensed Material to the 264 | extent reasonably practicable; 265 | 266 | b. indicate if You modified the Licensed Material and 267 | retain an indication of any previous modifications; and 268 | 269 | c. indicate the Licensed Material is licensed under this 270 | Public License, and include the text of, or the URI or 271 | hyperlink to, this Public License. 272 | 273 | 2. You may satisfy the conditions in Section 3(a)(1) in any 274 | reasonable manner based on the medium, means, and context in 275 | which You Share the Licensed Material. For example, it may be 276 | reasonable to satisfy the conditions by providing a URI or 277 | hyperlink to a resource that includes the required 278 | information. 279 | 3. If requested by the Licensor, You must remove any of the 280 | information required by Section 3(a)(1)(A) to the extent 281 | reasonably practicable. 282 | 283 | b. ShareAlike. 284 | 285 | In addition to the conditions in Section 3(a), if You Share 286 | Adapted Material You produce, the following conditions also apply. 287 | 288 | 1. The Adapter's License You apply must be a Creative Commons 289 | license with the same License Elements, this version or 290 | later, or a BY-NC-SA Compatible License. 291 | 292 | 2. You must include the text of, or the URI or hyperlink to, the 293 | Adapter's License You apply. You may satisfy this condition 294 | in any reasonable manner based on the medium, means, and 295 | context in which You Share Adapted Material. 296 | 297 | 3. You may not offer or impose any additional or different terms 298 | or conditions on, or apply any Effective Technological 299 | Measures to, Adapted Material that restrict exercise of the 300 | rights granted under the Adapter's License You apply. 301 | 302 | 303 | Section 4 -- Sui Generis Database Rights. 304 | 305 | Where the Licensed Rights include Sui Generis Database Rights that 306 | apply to Your use of the Licensed Material: 307 | 308 | a. for the avoidance of doubt, Section 2(a)(1) grants You the right 309 | to extract, reuse, reproduce, and Share all or a substantial 310 | portion of the contents of the database for NonCommercial purposes 311 | only; 312 | 313 | b. if You include all or a substantial portion of the database 314 | contents in a database in which You have Sui Generis Database 315 | Rights, then the database in which You have Sui Generis Database 316 | Rights (but not its individual contents) is Adapted Material, 317 | including for purposes of Section 3(b); and 318 | 319 | c. You must comply with the conditions in Section 3(a) if You Share 320 | all or a substantial portion of the contents of the database. 321 | 322 | For the avoidance of doubt, this Section 4 supplements and does not 323 | replace Your obligations under this Public License where the Licensed 324 | Rights include other Copyright and Similar Rights. 325 | 326 | 327 | Section 5 -- Disclaimer of Warranties and Limitation of Liability. 328 | 329 | a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE 330 | EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS 331 | AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF 332 | ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, 333 | IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, 334 | WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR 335 | PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, 336 | ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT 337 | KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT 338 | ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. 339 | 340 | b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE 341 | TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, 342 | NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, 343 | INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, 344 | COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR 345 | USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN 346 | ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR 347 | DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR 348 | IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. 349 | 350 | c. The disclaimer of warranties and limitation of liability provided 351 | above shall be interpreted in a manner that, to the extent 352 | possible, most closely approximates an absolute disclaimer and 353 | waiver of all liability. 354 | 355 | 356 | Section 6 -- Term and Termination. 357 | 358 | a. This Public License applies for the term of the Copyright and 359 | Similar Rights licensed here. However, if You fail to comply with 360 | this Public License, then Your rights under this Public License 361 | terminate automatically. 362 | 363 | b. Where Your right to use the Licensed Material has terminated under 364 | Section 6(a), it reinstates: 365 | 366 | 1. automatically as of the date the violation is cured, provided 367 | it is cured within 30 days of Your discovery of the 368 | violation; or 369 | 370 | 2. upon express reinstatement by the Licensor. 371 | 372 | For the avoidance of doubt, this Section 6(b) does not affect any 373 | right the Licensor may have to seek remedies for Your violations 374 | of this Public License. 375 | 376 | c. For the avoidance of doubt, the Licensor may also offer the 377 | Licensed Material under separate terms or conditions or stop 378 | distributing the Licensed Material at any time; however, doing so 379 | will not terminate this Public License. 380 | 381 | d. Sections 1, 5, 6, 7, and 8 survive termination of this Public 382 | License. 383 | 384 | 385 | Section 7 -- Other Terms and Conditions. 386 | 387 | a. The Licensor shall not be bound by any additional or different 388 | terms or conditions communicated by You unless expressly agreed. 389 | 390 | b. Any arrangements, understandings, or agreements regarding the 391 | Licensed Material not stated herein are separate from and 392 | independent of the terms and conditions of this Public License. 393 | 394 | 395 | Section 8 -- Interpretation. 396 | 397 | a. For the avoidance of doubt, this Public License does not, and 398 | shall not be interpreted to, reduce, limit, restrict, or impose 399 | conditions on any use of the Licensed Material that could lawfully 400 | be made without permission under this Public License. 401 | 402 | b. To the extent possible, if any provision of this Public License is 403 | deemed unenforceable, it shall be automatically reformed to the 404 | minimum extent necessary to make it enforceable. If the provision 405 | cannot be reformed, it shall be severed from this Public License 406 | without affecting the enforceability of the remaining terms and 407 | conditions. 408 | 409 | c. No term or condition of this Public License will be waived and no 410 | failure to comply consented to unless expressly agreed to by the 411 | Licensor. 412 | 413 | d. Nothing in this Public License constitutes or may be interpreted 414 | as a limitation upon, or waiver of, any privileges and immunities 415 | that apply to the Licensor or You, including from the legal 416 | processes of any jurisdiction or authority. 417 | 418 | ======================================================================= 419 | 420 | Creative Commons is not a party to its public licenses. 421 | Notwithstanding, Creative Commons may elect to apply one of its public 422 | licenses to material it publishes and in those instances will be 423 | considered the "Licensor." Except for the limited purpose of indicating 424 | that material is shared under a Creative Commons public license or as 425 | otherwise permitted by the Creative Commons policies published at 426 | creativecommons.org/policies, Creative Commons does not authorize the 427 | use of the trademark "Creative Commons" or any other trademark or logo 428 | of Creative Commons without its prior written consent including, 429 | without limitation, in connection with any unauthorized modifications 430 | to any of its public licenses or any other arrangements, 431 | understandings, or agreements concerning use of licensed material. For 432 | the avoidance of doubt, this paragraph does not form part of the public 433 | licenses. 434 | 435 | Creative Commons may be contacted at creativecommons.org. 436 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Intermediate Python 2 | 3 | Python - это невероятный язык с опытным и дружелюбным сообществом 4 | программистов. Тем не менее, на данный момент серьезно не хватает информации 5 | о хороших источниках, за которые можно было бы взяться после изучения основ 6 | языка. Постараюсь решить данную проблему с помощью этой книги. Я предлагаю вам 7 | краткое изложение нескольких интересных тем, которые вы сможете позднее изучить 8 | самостоятельно в подробностях. 9 | 10 | Темы, которые будут затронуты в этой книге, помогут вам открыть для себя 11 | множество потайных уголков языка. В конечном итоге я пишу эту книгу как 12 | справочник, которое я бы сам хотел иметь, когда начинал свой путь в Python. 13 | 14 | Начинающий, продвинутый и даже опытный программист найдет здесь что-нибудь 15 | полезное для себя. 16 | 17 | Однако помните, что данная книга не представляет из себя руководство и у неё 18 | нет цели научить вас Python. Затронутые темы не рассматриваются в подробностях, 19 | и лишь минимум необходимой информации предлагается вашему вниманию. 20 | 21 | Данная книга находится в постоянной доработке. Если вы найдете 22 | что-нибудь, что можно было бы улучшить (а я знаю, вам попадется немало таких 23 | мест), то отправляйте [pull request][1] или [открывайте issue][2]. 24 | 25 | Скажу даже больше - если вы хотите добавить новые главы, то pull request опять 26 | поможет, и я буду очень рад расширить книгу. 27 | 28 | Уверен, вам уже не терпится так же, как и мне, так что давайте приступим! 29 | 30 | > **Примечание:** Если вы хотите отблагодарить автора за работу, то 31 | отличным вариантом будет купить специальную версию книги на 32 | [Gumroad](https://gum.co/intermediate_python). Помимо этого, если книга 33 | оказалась вам полезной, то поделитесь с [автором](mailto:yasoob.khld@gmail.com) 34 | своим опытом (на английском, пожалуйста). Он будет очень рад прочесть все 35 | ваши письма. 36 | 37 | ## Книга 38 | 39 | - [Онлайн версия на gitbook][3] 40 | - [Содержание](SUMMARY.md) 41 | 42 | ## Автор 43 | 44 | - [Muhammad Yasoob Ullah Khalid](https://github.com/yasoob) 45 | 46 | ## Благодарности 47 | 48 | - [Philipp Hagemeister](https://github.com/phihag): 49 | 50 | Написал главу о функции `open`. Спасибо Филипп! 51 | 52 | ## Перевод 53 | 54 | Если вы хотите перевести книгу на другой язык - [дайте знать автору][4]. Он 55 | будет рад расширению аудитории. Список доступных переводов на настоящий момент: 56 | 57 | - [Английский](https://github.com/yasoob/intermediatePython) 58 | - [Китайский](https://github.com/eastlakeside/interpy-zh) 59 | - [Русский](https://github.com/lancelote/interpy-ru) 60 | - [Корейский](https://github.com/DDanggle/interpy-kr) 61 | - [Португальский](https://github.com/joanasouza/intermediatePython) 62 | 63 | ## Отзывы, предложения и помощь 64 | 65 | - [Как помочь развитию книги](CONTRIBUTING.md) 66 | - [Оставить свой отзыв](https://github.com/lancelote/interpy-ru/issues/new) 67 | 68 | ## Лицензия 69 | 70 | Данная книга распространяется под лицензией [Creative Commons][5] 71 | (CC BY-NC-SA 4.0). 72 | 73 | Если вы используете сами или рекомендуете эту книгу кому-нибудь еще - 74 | [расскажите об этом автору](mailto:yasoob.khld@gmail.com). 75 | 76 | [1]: https://github.com/lancelote/interpy-ru/pull/new/master 77 | [2]: https://github.com/lancelote/interpy-ru/issues/new 78 | [3]: https://pavel-karateev.gitbook.io/intermediate-python/ 79 | [4]: mailto:yasoob.khld@gmail.com 80 | [5]: http://creativecommons.org/licenses/by-nc-sa/4.0/ 81 | -------------------------------------------------------------------------------- /SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Содержание 2 | 3 | ## Средства разработки 4 | 5 | * [Виртуальное окружение](book/virtual_environment.md) 6 | * [Отладка](book/debugging.md) 7 | * [Анализ объекта](book/object_introspection.md) 8 | 9 | ## Синтаксис 10 | 11 | * [Исключения](book/exceptions.md) 12 | * [for - else](book/for_-_else.md) 13 | * [Тернарные операторы](book/ternary_operators.md) 14 | * [global и return](book/global_&_return.md) 15 | * [Функция open](book/open_function.md) 16 | * [\*args и \*\*kwargs](book/args_and_kwargs.md) 17 | * [Менеджеры контекста](book/context_managers.md) 18 | 19 | ## Функциональное программирование 20 | 21 | * [enumerate](book/enumerate.md) 22 | * [Анонимные функции](book/lambdas.md) 23 | * [Структура данных ``set``](book/set_-_data_structure.md) 24 | * [map и filter](book/map_filter.md) 25 | * [Абстракция списков](book/comprehensions.md) 26 | 27 | ## Структуры данных 28 | 29 | * [Генераторы](book/generators.md) 30 | * [Корутины](book/coroutines.md) 31 | * [Классы](book/classes.md) 32 | 33 | ## Типы данных 34 | 35 | * [collections](book/collections.md) 36 | * [Изменяемость](book/mutation.md) 37 | * [Магия \_\_slots\_\_](book/__slots__magic.md) 38 | 39 | ## Декораторы 40 | 41 | * [Что такое декоратор?](book/decorators.md) 42 | * [Кэширование функций](book/function_caching.md) 43 | 44 | ## Разное 45 | 46 | * [Однострочники](book/one_liners.md) 47 | * [Python C расширения](book/python_c_extension.md) 48 | * [Разработка под Python 2+3](book/targeting_python_2_3.md) 49 | -------------------------------------------------------------------------------- /_static/cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lancelote/interpy-ru/bec2931e23cbff9cbd02663fca3763b895e8d479/_static/cover.png -------------------------------------------------------------------------------- /book/__slots__magic.md: -------------------------------------------------------------------------------- 1 | # Магия \_\_slots\_\_ 2 | 3 | В Python каждый класс может иметь атрибуты экземпляров. По умолчанию, Python 4 | использует словарь для хранения атрибутов объекта. Это очень удобно, поскольку 5 | позволяет добавлять новые атрибуты во время исполнения программы. 6 | 7 | Однако, для небольших классов с известными атрибутами такой подход может стать 8 | проблемой. Словари занимают большой объём оперативной памяти. Python не может 9 | просто выделить заданное количество памяти при создании объекта для хранения 10 | его атрибутов. Поэтому большое число объектов будет занимать много оперативной 11 | памяти (я говорю о тысячах и миллионах). Тем не менее, существует способ 12 | обойти эту проблему. Он включает использование `__slots__`, чтобы Python не 13 | создавал словари под хранение атрибутов, а выделял заданный объём памяти для 14 | ограниченного числа атрибутов. Вот пример с использованием `__slots__` и без: 15 | 16 | **Без** `__slots__`: 17 | 18 | ```python 19 | class MyClass(object): 20 | def __init__(self, name, identifier): 21 | self.name = name 22 | self.identifier = identifier 23 | self.set_up() 24 | # ... 25 | ``` 26 | 27 | **С использованием** `__slots__`: 28 | 29 | ```python 30 | class MyClass(object): 31 | __slots__ = ['name', 'identifier'] 32 | def __init__(self, name, identifier): 33 | self.name = name 34 | self.identifier = identifier 35 | self.set_up() 36 | # ... 37 | ``` 38 | 39 | Второй пример кода уменьшит потребление оперативной памяти. Некоторые люди 40 | отмечают 40 и 50% сокращение потребления оперативной памяти при использовании 41 | этого решения. 42 | 43 | Как вариант, вам может дать шанс PyPy. Он выполняет подобную оптимизацию 44 | по умолчанию. 45 | 46 | Ниже я привожу пример замера потребления оперативной памяти "с" и без 47 | использования `__slots__`, выполненного в IPython, спасибо 48 | https://github.com/ianozsvald/ipython_memory_usage 49 | 50 | ```python 51 | Python 3.4.3 (default, Jun 6 2015, 13:32:34) 52 | Type "copyright", "credits" or "license" for more information. 53 | 54 | IPython 4.0.0 -- An enhanced Interactive Python. 55 | ? -> Introduction and overview of IPython's features. 56 | %quickref -> Quick reference. 57 | help -> Python's own help system. 58 | object? -> Details about 'object', use 'object??' for extra details. 59 | 60 | In [1]: import ipython_memory_usage.ipython_memory_usage as imu 61 | 62 | In [2]: imu.start_watching_memory() 63 | In [2] used 0.0000 MiB RAM in 5.31s, peaked 0.00 MiB above current, total RAM usage 15.57 MiB 64 | 65 | In [3]: %cat slots.py 66 | class MyClass(object): 67 | __slots__ = ['name', 'identifier'] 68 | def __init__(self, name, identifier): 69 | self.name = name 70 | self.identifier = identifier 71 | 72 | num = 1024*256 73 | x = [MyClass(1,1) for i in range(num)] 74 | In [3] used 0.2305 MiB RAM in 0.12s, peaked 0.00 MiB above current, total RAM usage 15.80 MiB 75 | 76 | In [4]: from slots import * 77 | In [4] used 9.3008 MiB RAM in 0.72s, peaked 0.00 MiB above current, total RAM usage 25.10 MiB 78 | 79 | In [5]: %cat noslots.py 80 | class MyClass(object): 81 | def __init__(self, name, identifier): 82 | self.name = name 83 | self.identifier = identifier 84 | 85 | num = 1024*256 86 | x = [MyClass(1,1) for i in range(num)] 87 | In [5] used 0.1758 MiB RAM in 0.12s, peaked 0.00 MiB above current, total RAM usage 25.28 MiB 88 | 89 | In [6]: from noslots import * 90 | In [6] used 22.6680 MiB RAM in 0.80s, peaked 0.00 MiB above current, total RAM usage 47.95 MiB 91 | ``` 92 | -------------------------------------------------------------------------------- /book/args_and_kwargs.md: -------------------------------------------------------------------------------- 1 | # \*args и \*\*kwargs 2 | 3 | Я много раз замечал, что у новых Python разработчиков вызывают трудности 4 | магические переменные \*args и \*\*kwargs. Так что же они из себя представляют? 5 | Во-первых, позвольте сказать, что вам не обязательно называть их именно 6 | \*args и \*\*kwargs. Только `*` (звездочка) действительно необходима. 7 | К примеру, их можно назвать \*var и \*\*vars. Использование \*args и \*\*kwargs 8 | идет на уровне соглашения между разработчиками. Теперь давайте начнём рассказ 9 | с \*args. 10 | 11 | ## Использование \*args 12 | 13 | \*args и \*\*kwargs в основном используются в определениях функций. \*args 14 | и \*\*kwargs позволяют передавать им произвольное число аргументов. Под 15 | произвольным числом здесь понимается ситуация, когда вы не знаете заранее 16 | сколько аргументов может быть передано функции пользователем, поэтому в данном 17 | случае вам необходимо использовать эти ключевые слова. \*args используется 18 | для передачи произвольного числа **неименованных** аргументов функции. Вот 19 | пример, чтобы помочь вам понять идею: 20 | 21 | ```python 22 | def test_var_args(f_arg, *argv): 23 | print("Первый позиционный аргумент:", f_arg) 24 | for arg in argv: 25 | print("Другой аргумент из *argv:", arg) 26 | 27 | test_var_args('yasoob', 'python', 'eggs', 'test') 28 | ``` 29 | 30 | Результат будет таким: 31 | 32 | ``` 33 | Первый позиционный аргумент: yasoob 34 | Другой аргумент из *argv: python 35 | Другой аргумент из *argv: eggs 36 | Другой аргумент из *argv: test 37 | ``` 38 | 39 | Надеюсь, пример развеял возможную путаницу. Теперь перейдем к разговору о 40 | \*\*kwargs 41 | 42 | ## Использование \*\*kwargs 43 | 44 | \*\*kwargs позволяет вам передавать произвольное число **именованных** 45 | аргументов в функцию. Таким образом, вам необходимо использовать \*\*kwargs там, 46 | где вы хотите работать с **именованными** аргументами. Очередной пример: 47 | 48 | ```python 49 | def greet_me(**kwargs): 50 | for key, value in kwargs.items(): 51 | print("{0} = {1}".format(key, value)) 52 | 53 | >>> greet_me(name="yasoob") 54 | name = yasoob 55 | ``` 56 | 57 | В результате, мы оперируем произвольным числом именованных аргументов в нашей 58 | функции. Это были основы использования \*\*kwargs и можете сами отметить 59 | насколько это может быть удобно в определенных ситуациях. Теперь давайте 60 | рассмотрим использование \*args и \*\*kwargs для передачи в функцию списка или 61 | словаря аргументов. 62 | 63 | ## Использование \*args и \*\*kwargs при вызове функции 64 | 65 | Рассмотрим вызов функции, используя \*args и \*\*kwargs. Пусть у нас есть вот 66 | такая небольшая функция: 67 | 68 | ```python 69 | def test_args_kwargs(arg1, arg2, arg3): 70 | print("arg1:", arg1) 71 | print("arg2:", arg2) 72 | print("arg3:", arg3) 73 | ``` 74 | 75 | Мы можем использовать \*args или \*\*kwargs чтобы передать в неё аргументы. 76 | Вот как мы это сделаем: 77 | 78 | ```python 79 | # Сначала с *args 80 | >>> args = ("two", 3, 5) 81 | >>> test_args_kwargs(*args) 82 | arg1: two 83 | arg2: 3 84 | arg3: 5 85 | 86 | # Теперь с **kwargs: 87 | >>> kwargs = {"arg3": 3, "arg2": "two", "arg1": 5} 88 | >>> test_args_kwargs(**kwargs) 89 | arg1: 5 90 | arg2: two 91 | arg3: 3 92 | ``` 93 | 94 | ## Порядок использования \*args, \*\*kwargs и формальных параметров 95 | 96 | Если вы хотите использовать все три метода в функции, то порядок должен быть 97 | таким: 98 | 99 | ```python 100 | some_func(fargs, *args, **kwargs) 101 | ``` 102 | 103 | ## Когда их использовать? 104 | 105 | Все зависит от ваших потребностей. Наиболее часто \*args и \*\*kwargs 106 | используются при написании декораторов (подробнее о декораторах в другой главе). 107 | Помимо этого данная методика может использоваться для "monkey patching". Под 108 | "monkey patching" понимается модификация кода во время выполнения программы. 109 | Предположим, у нас есть класс с методом `get_info`, который обращается к API 110 | и возвращает данные ответа. Для тестирования этого метода мы можем заменить 111 | вызов API и искусственно возвращать определенные тестовые данные. Например: 112 | 113 | ```python 114 | import someclass 115 | 116 | def get_info(self, *args): 117 | return "Test data" 118 | 119 | someclass.get_info = get_info 120 | ``` 121 | 122 | Уверен, вы можете придумать и другие подходящие сценарии использования. 123 | -------------------------------------------------------------------------------- /book/classes.md: -------------------------------------------------------------------------------- 1 | # Классы 2 | 3 | Классы - это ядро Python. Они дают широкие возможности, но их легко неправильно 4 | использовать. В этой главе я расскажу про некоторые трюки в работе с классами и 5 | сделаю несколько предостережений. Давайте начнем! 6 | 7 | ## Переменные экземпляра и класса 8 | 9 | Большинство начинающих, а иногда и опытных Python разработчиков не понимают до 10 | конца различия между переменными экземпляра и переменными класса. Это приводит 11 | к некорректному использованию этих различных типов переменных. Давайте 12 | разберемся. 13 | 14 | Основное различие: 15 | 16 | - Переменные экземпляров предназначены для данных уникальных для каждого 17 | объекта 18 | - Переменные класса - для общих для всех экземпляров класса данных 19 | 20 | Посмотрим на пример: 21 | 22 | ```python 23 | class Cal(object): 24 | # pi - переменная класса 25 | pi = 3.142 26 | 27 | def __init__(self, radius): 28 | # self.radius - переменная экземпляра 29 | self.radius = radius 30 | 31 | def area(self): 32 | return self.pi * (self.radius ** 2) 33 | 34 | a = Cal(32) 35 | a.area() 36 | # Вывод: 3217.408 37 | a.pi 38 | # Вывод: 3.142 39 | a.pi = 43 40 | a.pi 41 | # Вывод: 43 42 | 43 | b = Cal(44) 44 | b.area() 45 | # Вывод: 6082.912 46 | b.pi 47 | # Вывод: 3.142 48 | b.pi = 50 49 | b.pi 50 | # Вывод: 50 51 | ``` 52 | 53 | С использованием изменяемых переменных класса редко бывают проблемы. Поэтому 54 | разработчики обычно не стараются изучить тему подробнее - все и так работает! 55 | Если вы тоже уверены в надежности использования таких переменных, то следующий 56 | пример для вас: 57 | 58 | ```python 59 | class SuperClass(object): 60 | superpowers = [] 61 | 62 | def __init__(self, name): 63 | self.name = name 64 | 65 | def add_superpower(self, power): 66 | self.superpowers.append(power) 67 | 68 | foo = SuperClass('foo') 69 | bar = SuperClass('bar') 70 | foo.name 71 | # Вывод: 'foo' 72 | 73 | bar.name 74 | # Вывод: 'bar' 75 | 76 | foo.add_superpower('fly') 77 | bar.superpowers 78 | # Вывод: ['fly'] 79 | 80 | foo.superpowers 81 | # Вывод: ['fly'] 82 | ``` 83 | 84 | Красота неправильного использования изменяемых переменных класса. Для 85 | предотвращения подобных ошибок - не храните изменяемые структуры данных в 86 | переменных класса или понимайте зачем вам это нужно. 87 | 88 | ## Классы нового стиля 89 | 90 | Классы нового стиля были представлены в Python 2.1, но не так много 91 | разработчиков знает о них даже сейчас! Отчасти это связано с сохранением 92 | поддержки классов старого стиля для обратной совместимости. Давайте 93 | рассмотрим разницу между двумя стилями: 94 | 95 | - Классы старого стиля ничему не наследуют 96 | - Классы нового стиля наследуют `object` 97 | 98 | Базовый пример: 99 | 100 | ```python 101 | class OldClass(): 102 | def __init__(self): 103 | print('Я старый класс') 104 | 105 | class NewClass(object): 106 | def __init__(self): 107 | print('Я новый модный класс') 108 | 109 | old = OldClass() 110 | # Вывод: Я старый класс 111 | 112 | new = NewClass() 113 | # Вывод: Я новый модный класс 114 | ``` 115 | 116 | Наследование от `object` дает новым классам доступ к определенной *магии*. 117 | Например, вы можете использовать оптимизацию `__slots__`, вызов `super()` 118 | и дескрипторы. Резюме? Всегда используйте классы нового стиля. 119 | 120 | **Примечание:** в Python 3 все классы нового стиля. Не важно наследует ли 121 | ваш класс `object` или нет. Тем не менее, хорошей идеей будет явно прописывать 122 | наследование. 123 | 124 | ## Магические методы 125 | 126 | Классы в Python известны за свои магические методы, их отличительная черта - 127 | двойное нижнее подчеркивание с двух сторон от имени. Давайте рассмотрим 128 | несколько из них. 129 | 130 | ### `__init__` 131 | 132 | Это конструктор класса. Конструктор вызывается каждый раз при создании 133 | экземпляра класса. Например: 134 | 135 | ```python 136 | class GetTest(object): 137 | def __init__(self): 138 | print('Приветствую!') 139 | 140 | def another_method(self): 141 | print('Я другой метод, который не вызывается автоматически') 142 | 143 | a = GetTest() 144 | # Вывод: Приветствую! 145 | 146 | a.another_method() 147 | # Вывод: Я другой метод, который не вызывается автоматически 148 | ``` 149 | 150 | Как вы видите `__init__` вызывается при создании экземпляра класса. Вы 151 | также можете передавать аргументы конструктору для инициализации экземпляра: 152 | 153 | ```python 154 | class GetTest(object): 155 | def __init__(self, name): 156 | print('Приветствую! {0}'.format(name)) 157 | 158 | def another_method(self): 159 | print('Я другой метод, который не вызывается автоматически') 160 | 161 | a = GetTest('Yasoob') 162 | # Output: Приветствую! Yasoob 163 | 164 | # Попробуем создать экземпляр без аргумента "name" 165 | b = GetTest() 166 | Traceback (most recent call last): 167 | File "", line 1, in 168 | TypeError: __init__() takes exactly 2 arguments (1 given) 169 | ``` 170 | 171 | Надеюсь в общих чертах логика работы `__init__` понятна. 172 | 173 | ### ``__getitem__`` 174 | 175 | Реализация метода `__getitem__` в классе позволяет использовать на его 176 | экземплярах `[]` оператор. Пример: 177 | 178 | ```python 179 | class GetTest(object): 180 | def __init__(self): 181 | self.info = { 182 | 'name':'Yasoob', 183 | 'country':'Pakistan', 184 | 'number':12345812 185 | } 186 | 187 | def __getitem__(self,i): 188 | return self.info[i] 189 | 190 | foo = GetTest() 191 | 192 | foo['name'] 193 | # Вывод: 'Yasoob' 194 | 195 | foo['number'] 196 | # Вывод: 12345812 197 | ``` 198 | 199 | Без реализации `__getitem__` вы бы получили следующую ошибку: 200 | 201 | ``` 202 | >>> foo['name'] 203 | 204 | Traceback (most recent call last): 205 | File "", line 1, in 206 | TypeError: 'GetTest' object has no attribute '__getitem__' 207 | ``` 208 | -------------------------------------------------------------------------------- /book/collections.md: -------------------------------------------------------------------------------- 1 | # Collections 2 | 3 | В стандартную библиотеку Python входит модуль `collections` содержащий 4 | дополнительные структуры данных. Мы поговорим о некоторых и обсудим их пользу. 5 | 6 | А конкретно: 7 | 8 | - `defaultdict` 9 | - `OrderedDict` 10 | - `counter` 11 | - `deque` 12 | - `namedtuple` 13 | - `enum.Enum` (вне модуля; Python 3.4+) 14 | 15 | ## `defaultdict` 16 | 17 | Я использую `defaultdict` время от времени. В отличие от `dict` нам не 18 | нужно проверять существует ли ключ в словаре или нет. В результате мы можем 19 | писать следующий код: 20 | 21 | ```python 22 | from collections import defaultdict 23 | 24 | colours = ( 25 | ('Yasoob', 'Yellow'), 26 | ('Ali', 'Blue'), 27 | ('Arham', 'Green'), 28 | ('Ali', 'Black'), 29 | ('Yasoob', 'Red'), 30 | ('Ahmed', 'Silver'), 31 | ) 32 | 33 | favourite_colours = defaultdict(list) 34 | 35 | for name, colour in colours: 36 | favourite_colours[name].append(colour) 37 | 38 | print(favourite_colours) 39 | 40 | # Вывод: 41 | # defaultdict(, 42 | # {'Arham': ['Green'], 43 | # 'Yasoob': ['Yellow', 'Red'], 44 | # 'Ahmed': ['Silver'], 45 | # 'Ali': ['Blue', 'Black'] 46 | # }) 47 | ``` 48 | 49 | Другим популярным случаем использования `defaultdict` является добавление 50 | элементов в список внутри словаря. Если ключ не существует в словаре, то вы 51 | упрётесь в `KeyError`. `defaultdict` позволяет обойти эту проблему 52 | аккуратным образом. Для начала, позвольте привести пример использования 53 | `dict` с исключением `KeyError`, а затем мы посмотрим на пример с 54 | `defaultdict`. 55 | 56 | **Проблема:** 57 | 58 | ```python 59 | some_dict = {} 60 | some_dict['colours']['favourite'] = "yellow" 61 | # Вызывает KeyError: 'colours' 62 | ``` 63 | 64 | **Решение:** 65 | 66 | ```python 67 | import collections 68 | tree = lambda: collections.defaultdict(tree) 69 | some_dict = tree() 70 | some_dict['colours']['favourite'] = "yellow" 71 | # Работает без ошибок 72 | ``` 73 | 74 | Вы можете вывести в консоль `some_dict` используя `json.dumps`. Вот пример: 75 | 76 | ```python 77 | import json 78 | print(json.dumps(some_dict)) 79 | # Вывод: {"colours": {"favourite": "yellow"}} 80 | ``` 81 | 82 | ## `OrderedDict` 83 | 84 | `OrderedDict` сохраняет элементы в порядке добавление в словарь. Изменение 85 | значения ключа не изменяет его позиции. При этом удаление и повторное 86 | добавление перенесет ключ в конец словаря. 87 | 88 | **Проблема:** 89 | 90 | ```python 91 | colours = {"Red": 198, "Green": 170, "Blue": 160} 92 | for key, value in colours.items(): 93 | print(key, value) 94 | # Вывод: 95 | # Red 198 96 | # Blue 160 97 | # Green 170 98 | # 99 | # Элементы выводятся в произвольном порядке 100 | ``` 101 | 102 | **Решение:** 103 | 104 | ```python 105 | from collections import OrderedDict 106 | 107 | colours = OrderedDict([("Red", 198), ("Green", 170), ("Blue", 160)]) 108 | for key, value in colours.items(): 109 | print(key, value) 110 | # Вывод: 111 | # Red 198 112 | # Green 170 113 | # Blue 160 114 | # 115 | # Порядок элементов сохранен 116 | ``` 117 | 118 | ## `counter` 119 | 120 | `Counter` позволяет подсчитывать частоту определенных элементов. К примеру, 121 | мы можем использовать его, чтобы посчитать сколько любимых цветов у каждого 122 | человека: 123 | 124 | ```python 125 | from collections import Counter 126 | 127 | colours = ( 128 | ('Yasoob', 'Yellow'), 129 | ('Ali', 'Blue'), 130 | ('Arham', 'Green'), 131 | ('Ali', 'Black'), 132 | ('Yasoob', 'Red'), 133 | ('Ahmed', 'Silver'), 134 | ) 135 | 136 | favs = Counter(name for name, colour in colours) 137 | print(favs) 138 | # Вывод: Counter({ 139 | # 'Yasoob': 2, 140 | # 'Ali': 2, 141 | # 'Arham': 1, 142 | # 'Ahmed': 1 143 | # }) 144 | ``` 145 | 146 | Мы также можем посчитать частоту строк в файле. Пример: 147 | 148 | ```python 149 | with open('filename', 'rb') as f: 150 | line_count = Counter(f) 151 | print(line_count) 152 | ``` 153 | 154 | ## `deque` 155 | 156 | `deque` предлагает нам двустороннюю очередь, которая позволяет добавлять 157 | и удалять элементы с обеих сторон. Для начала, вам нужно импортировать 158 | модуль `deque` из библиотеки `collections`: 159 | 160 | ```python 161 | from collections import deque 162 | ``` 163 | 164 | Теперь мы можем создать экземпляр двусторонней очереди: 165 | 166 | ```python 167 | d = deque() 168 | ``` 169 | 170 | Очередь работает подобно списку в Python и имеет схожие методы. Например, вы 171 | можете: 172 | 173 | ```python 174 | d = deque() 175 | d.append('1') 176 | d.append('2') 177 | d.append('3') 178 | 179 | print(len(d)) 180 | # Вывод: 3 181 | 182 | print(d[0]) 183 | # Вывод: '1' 184 | 185 | print(d[-1]) 186 | # Вывод: '3' 187 | ``` 188 | 189 | Мы можем отрезать элементы с обеих сторон очереди: 190 | 191 | ```python 192 | d = deque(range(5)) 193 | print(len(d)) 194 | # Вывод: 5 195 | 196 | d.popleft() 197 | # Вывод: 0 198 | 199 | d.pop() 200 | # Вывод: 4 201 | 202 | print(d) 203 | # Вывод: deque([1, 2, 3]) 204 | ``` 205 | 206 | Мы также можем ограничить число элементов, которые может хранить очередь. 207 | Таким образом при достижении максимального числа элементов очередь начнет 208 | отрезать элементы с другого конца. Это проще объяснить на примере: 209 | 210 | ```python 211 | d = deque(maxlen=30) 212 | ``` 213 | 214 | Теперь, когда мы попытаемся добавить 31-й элемент - очередь отрежет первый элемент 215 | с другого конца. Вы также можете добавлять элементы к очереди с обоих концов: 216 | 217 | ```python 218 | d = deque([1,2,3,4,5]) 219 | d.extendleft([0]) 220 | d.extend([6,7,8]) 221 | print(d) 222 | # Вывод: deque([0, 1, 2, 3, 4, 5, 6, 7, 8]) 223 | ``` 224 | 225 | ## `namedtuple` 226 | 227 | Вы уже должны быть знакомы с кортежами. Кортеж в Python это неизменяемый 228 | список, который позволяет хранить объекты, разделенные запятой. Они практически 229 | идентичны спискам, за исключением нескольких важных особенностей. В отличие от 230 | списков, **вы не можете изменить элемент кортежа**. В то же время 231 | вы можете обращаться к элементам кортежа по индексам: 232 | 233 | ```python 234 | man = ('Ali', 30) 235 | print(man[0]) 236 | # Вывод: Ali 237 | ``` 238 | 239 | Отлично, так что же тогда `namedtuples`? Этот модуль открывает доступ к 240 | удобной структуре данных для простых задач. С помощью именованных кортежей вам 241 | не обязательно использовать индексы для обращения к элементам кортежа. Вы 242 | можете думать об именованных кортежах как о словарях, но в отличие от словарей 243 | они неизменяемы. 244 | 245 | ```python 246 | from collections import namedtuple 247 | 248 | Animal = namedtuple('Animal', 'name age type') 249 | perry = Animal(name="perry", age=31, type="cat") 250 | 251 | print(perry) 252 | # Вывод: Animal(name='perry', age=31, type='cat') 253 | 254 | print(perry.name) 255 | # Вывод: 'perry' 256 | ``` 257 | 258 | Теперь вы можете видеть, что мы можем обращаться к элементам именованного кортежа при 259 | помощи их имени и `.` (точки). Давайте чуть подробнее на этом остановимся. 260 | Именованный кортеж имеет два обязательных аргумента. Это имя самого кортежа и 261 | имена полей кортежа. В примере выше имя нашего кортежа `Animal`, имена 262 | полей соответственно: `name`, `age` и `type`. Именованный кортеж позволяет 263 | создавать **само-документированные** кортежи. Вы сможете легко понять код 264 | при первом же взгляде на него. И, поскольку вы не привязаны к индексам, у вас 265 | открывается больше возможностей по поддержке своего кода. Помимо этого, 266 | **именованные кортежи не создают словари для каждого экземпляра**, они легковесны и 267 | не требуют больше памяти чем обычные кортежи. Это делает их быстрее словарей. 268 | Тем не менее, помните, что как и в случае с обычными кортежами, **именованный 269 | кортеж неизменяем**. Это означает, что такой код работать не будет: 270 | 271 | ```python 272 | from collections import namedtuple 273 | 274 | Animal = namedtuple('Animal', 'name age type') 275 | perry = Animal(name="perry", age=31, type="cat") 276 | perry.age = 42 277 | 278 | # Вывод: Traceback (most recent call last): 279 | # File "", line 1, in 280 | # AttributeError: can't set attribute 281 | ``` 282 | 283 | Вы должны использовать именованные кортежи для улучшения читаемости кода. 284 | Они **обратносовместимы с обычными кортежами**. Это значит, что вы можете 285 | использовать численные индексы с именованными кортежами: 286 | 287 | ```python 288 | from collections import namedtuple 289 | 290 | Animal = namedtuple('Animal', 'name age type') 291 | perry = Animal(name="perry", age=31, type="cat") 292 | print(perry[0]) 293 | # Вывод: perry 294 | ``` 295 | 296 | И последнее, вы можете сконвертировать именованный кортеж в словарь. Вот так: 297 | 298 | ```python 299 | from collections import namedtuple 300 | 301 | Animal = namedtuple('Animal', 'name age type') 302 | perry = Animal(name="Perry", age=31, type="cat") 303 | print(perry._asdict()) 304 | # Вывод: OrderedDict([('name', 'Perry'), ('age', 31), ... 305 | ``` 306 | 307 | ## `enum.Enum` (Python 3.4+) 308 | 309 | Другой полезной структурой данных является `enum`. Он доступен в модуле 310 | `enum`, начиная с Python 3.4 (также в PyPI как бекпорт под именем `enum34`). 311 | Enums ([перечисляемый тип](https://en.wikipedia.org/wiki/Enumerated_type)) 312 | это простой способ организации разных вещей. 313 | 314 | Давайте рассмотрим именованный кортеж `Animal` из прошлого примера. У него 315 | есть поле `type`. Проблема в том, что его тип - строка. Это создаёт нам 316 | несколько проблем. Что если пользователь ввёл `Cat`, поскольку нажал Shift? 317 | Или `CAT`? Или `kitten`? 318 | 319 | Перечисление может помочь обойти эту проблему, позволив не использовать строки. 320 | Рассмотрим пример: 321 | 322 | ```python 323 | from collections import namedtuple 324 | from enum import Enum 325 | 326 | class Species(Enum): 327 | cat = 1 328 | dog = 2 329 | horse = 3 330 | aardvark = 4 331 | butterfly = 5 332 | owl = 6 333 | platypus = 7 334 | dragon = 8 335 | unicorn = 9 336 | # Список продолжается... 337 | 338 | # Нам безразличен возраст животного, так что мы используем синонимы 339 | kitten = 1 340 | puppy = 2 341 | 342 | Animal = namedtuple('Animal', 'name age type') 343 | perry = Animal(name="Perry", age=31, type=Species.cat) 344 | drogon = Animal(name="Drogon", age=4, type=Species.dragon) 345 | tom = Animal(name="Tom", age=75, type=Species.cat) 346 | charlie = Animal(name="Charlie", age=2, type=Species.cat) 347 | 348 | # А теперь несколько тестов 349 | >>> charlie.type == tom.type 350 | True 351 | >>> charlie.type 352 | 353 | ``` 354 | 355 | Так у нас куда меньше шансов допустить ошибку. При этом мы должны быть 356 | конкретны и использовать только перечисление для определения полей. 357 | 358 | Существует три способа получения доступа к перечисляемым элементам. Например, 359 | все три метода, представленные ниже, дадут вам значения поля `cat`: 360 | 361 | ```python 362 | Species(1) 363 | Species['cat'] 364 | Species.cat 365 | ``` 366 | 367 | Это было короткое погружение в библиотеку `collections`. Обязательно 368 | ознакомьтесь с официальной документацией после чтения этой главы. 369 | -------------------------------------------------------------------------------- /book/comprehensions.md: -------------------------------------------------------------------------------- 1 | # Абстракции списков/словарей/множеств 2 | 3 | Абстракции - это та особенность Python, 4 | которой мне будет очень сильно недоставать, если я сменю 5 | язык программирования. Абстракции - это конструкторы, позволяющие 6 | создавать последовательности из других последовательностей. В Python (2 и 3) 7 | есть три типа подобных абстракций: 8 | 9 | - абстракции списков 10 | - абстракции словарей 11 | - абстракции множеств 12 | - абстракции генераторов 13 | 14 | Мы обсудим все три, хотя освоив абстракции списков вы легко перенесёте знания 15 | на остальные типы. 16 | 17 | ## `list` абстракции 18 | 19 | Абстракции списков открывают доступ к простому и лаконичному синтаксису генерации 20 | списков. Конструкция состоит из квадратных скобок содержащих выражение и 21 | оператор `for`, плюс дополнительные `for` или `if` при необходимости. 22 | Выражения могут быть любыми, т.е. вам разрешается иметь любые элементы внутри 23 | списка. Результатом работы будет новый список после исполнения выражения с 24 | оглядкой на `for` и `if`. 25 | 26 | **Шаблон использования:** 27 | 28 | ```python 29 | variable = [out_exp for out_exp in input_list if out_exp == 2] 30 | ``` 31 | 32 | Вот короткий пример: 33 | 34 | ```python 35 | multiples = [i for i in range(30) if i % 3 == 0] 36 | print(multiples) 37 | # Вывод: [0, 3, 6, 9, 12, 15, 18, 21, 24, 27] 38 | ``` 39 | 40 | Такой синтаксис может быть очень удобен для быстрого создания списков. 41 | Использование такого подхода вместо функции 42 | `filter` позволяет повысить читаемость кода (без потери скорости исполнения). 43 | Особенно хорошо абстракции списков заменяют создание нового 44 | простого списка при помощи цикла `for` и `append`. Наглядный пример: 45 | 46 | ```python 47 | squared = [] 48 | for x in range(10): 49 | squared.append(x**2) 50 | ``` 51 | 52 | Вы можете сократить решение до одной строки: 53 | 54 | ```python 55 | squared = [x**2 for x in range(10)] 56 | ``` 57 | 58 | ## `dict` абстракции 59 | 60 | Абстракции словарей используются схожим образом. Вот пример, на который я недавно 61 | наткнулся: 62 | 63 | ```python 64 | mcase = {'a': 10, 'b': 34, 'A': 7, 'Z': 3} 65 | 66 | mcase_frequency = { 67 | k.lower(): mcase.get(k.lower(), 0) + mcase.get(k.upper(), 0) 68 | for k in mcase.keys() 69 | } 70 | 71 | # mcase_frequency == {'a': 17, 'z': 3, 'b': 34} 72 | ``` 73 | 74 | В примере выше мы суммируем значения ключей, которые отличаются только 75 | регистром. Лично я редко пользуюсь этим методом. Вы также можете легко 76 | поменять местами ключи и значения в словаре: 77 | 78 | ```python 79 | {v: k for k, v in some_dict.items()} 80 | ``` 81 | 82 | ## `set` абстракции 83 | 84 | Абстракции множеств схожи с абстракциями списков. Единственное различие - 85 | используются фигурные скобки `{}`. Вот пример: 86 | 87 | ```python 88 | squared = {x**2 for x in [1, 1, 2]} 89 | print(squared) 90 | # Вывод: {1, 4} 91 | ``` 92 | 93 | ## Абстракции генераторов 94 | 95 | Абстракции генераторов похожи на абстракции списков. Единственное отличие - генераторы не выделяют память сразу под все элементы, а возвращают их один за одним, что намного более экономно: 96 | 97 | ```python 98 | multiples_gen = (i for i in range(30) if i % 3 == 0) 99 | print(multiples_gen) 100 | # Output: at 0x7fdaa8e407d8> 101 | for x in multiples_gen: 102 | print(x) 103 | # Outputs numbers 104 | ``` 105 | 106 | ## Примечание 107 | 108 | В русскоязычном сегменте интернета распространение получили другие названия абстракций, в частности используются понятия `генераторы списков/словарей/множеств`. Данные термины, однако, не совсем корректны, так как не являются генераторами (здесь возможна путаница с `genexp`, т.е. `(x**2 for x in [1, 2, 3])` - генераторное выражение, возвращающее объект генератора). В английском используется `list/dict/set comprehension` (`comprehension` - включение). Рекомендуется использовать либо вариант `списковые включения`, либо английское название или его краткую форму `listcomp`/`dictcomp`/`setcomp`. 109 | -------------------------------------------------------------------------------- /book/context_managers.md: -------------------------------------------------------------------------------- 1 | # Менеджеры контекста 2 | 3 | Менеджеры контекста позволяют выделять и освобождать ресурсы строго по 4 | необходимости. Самый популярный пример использования менеджера контекста - 5 | выражение `with`. Предположим, у вас есть две связанные операции, которые 6 | вы хотите исполнить в паре, поместив между ними блок кода. Менеджеры контекста 7 | позволяют сделать именно это. Например: 8 | 9 | ```python 10 | with open('some_file', 'w') as opened_file: 11 | opened_file.write('Hola!') 12 | ``` 13 | 14 | Код выше открывает файл, записывает в него данные и закрывает файл после этого. 15 | При возникновении ошибки при записи данных в файл менеджер контекста 16 | попробует его закрыть. Этот код эквивалентен следующему: 17 | 18 | ```python 19 | file = open('some_file', 'w') 20 | try: 21 | file.write('Hola!') 22 | finally: 23 | file.close() 24 | ``` 25 | 26 | Сравнив с первым блоком кода, мы можем заметить замену шаблонного кода на 27 | `with`. Основное преимущество использования `with` - это гарантия закрытия 28 | файла вне зависимости от того, как будет завершён вложенный код. 29 | 30 | Распространенный паттерн использования контекстных менеджеров - блокирование и 31 | разблокирование ресурсов, а также закрытие открытых файлов (как я уже показал 32 | выше). 33 | 34 | Давайте посмотрим, как мы можем написать свой собственный менеджер контекста. 35 | Это позволит нам лучше понять логику его работы. 36 | 37 | ## Контекст-менеджер как класс 38 | 39 | Необходимый минимум функциональности контекстного менеджера требует методов 40 | `__enter__` и `__exit__`. Давайте напишем свой контекстный менеджер для 41 | работы с файлами и изучим основы. 42 | 43 | ```python 44 | class File(object): 45 | def __init__(self, file_name, method): 46 | self.file_obj = open(file_name, method) 47 | def __enter__(self): 48 | return self.file_obj 49 | def __exit__(self, type, value, traceback): 50 | self.file_obj.close() 51 | ``` 52 | 53 | Просто определив методы `__enter__` и `__exit__`, мы можем использовать 54 | новый контекстный менеджер с `with`. Давайте попробуем: 55 | 56 | ```python 57 | with File('demo.txt', 'w') as opened_file: 58 | opened_file.write('Hola!') 59 | ``` 60 | 61 | Метод `__exit__` принимает три аргумента. Они обязательны для любого метода 62 | `__exit__` класса контекстного менеджера. Давайте обсудим логику работы: 63 | 64 | 1. `with` сохраняет метод `__exit__` класса `File`. 65 | 2. Следует вызов метода `__enter__` класса `File`. 66 | 3. Метод `__enter__` открывает файл и возвращает его. 67 | 4. Дескриптор файла передается в `opened_file`. 68 | 5. Мы записываем информацию в файл при помощи `.write()`. 69 | 6. `with` вызывает сохраненный `__exit__` метод. 70 | 7. Метод `__exit__` закрывает файл. 71 | 72 | ## Обработка исключений 73 | 74 | Мы ещё не успели поговорить об аргументах `type`, `value` и `traceback` 75 | метода `__exit__`. Между четвертым и шестым шагом при возникновении 76 | исключения, Python передает тип, значение и обратную трассировку исключения 77 | методу `__exit__`. Это позволяет методу `__exit__` выбирать способ закрытия 78 | файла и выполнять дополнительные действия при необходимости. В нашем случае, 79 | мы не уделяем им особого внимания. 80 | 81 | Что если объект файла вызвал исключение? Возможно, мы пытаемся вызывать метод 82 | на объекте, который его не поддерживает. Например: 83 | 84 | ```python 85 | with File('demo.txt', 'w') as opened_file: 86 | opened_file.undefined_function('Hola!') 87 | ``` 88 | 89 | Давайте разберём шаги, которые выполняет `with` при возникновении 90 | исключения: 91 | 92 | 1. Тип, значение и обратная трассировка ошибки передается в метод 93 | `__exit__`. 94 | 2. Обработка исключения передается методу `__exit__` 95 | 3. Если `__exit__` возвращает `True`, то исключение было корректно обработано. 96 | 4. При возврате любого другого значения `with` вызывает исключение. 97 | 98 | В нашем случае метод `__exit__` возвращает `None` (при отсутствии 99 | выражения `return` метод в Python возвращает `None`). Таким образом, `with` 100 | вызывает исключение: 101 | 102 | ```python 103 | Traceback (most recent call last): 104 | File "", line 2, in 105 | AttributeError: 'file' object has no attribute 'undefined_function' 106 | ``` 107 | 108 | Давайте попробуем обработать исключение в методе `__exit__`: 109 | 110 | ```python 111 | class File(object): 112 | def __init__(self, file_name, method): 113 | self.file_obj = open(file_name, method) 114 | def __enter__(self): 115 | return self.file_obj 116 | def __exit__(self, type, value, traceback): 117 | print("Исключение было обработано") 118 | self.file_obj.close() 119 | return True 120 | 121 | with File('demo.txt', 'w') as opened_file: 122 | opened_file.undefined_function() 123 | 124 | # Вывод: Исключение было обработано 125 | ``` 126 | 127 | Наш метод `__exit__` возвращает `True`, таким образом `with` не вызывает 128 | исключение. 129 | 130 | Это не единственный способ реализации контекстных менеджеров - есть и другой 131 | и мы посмотрим на него в следующем параграфе. 132 | 133 | ## Контекст-менеджер из генератора 134 | 135 | Мы также можем реализовать менеджер контекста через декораторы и генераторы. 136 | В Python присутствует модуль `contextlib` специально для этой цели. Вместо 137 | написания класса, мы можем реализовать менеджер контекста из 138 | функции-генератора. Посмотрим на простой пример: 139 | 140 | ```python 141 | from contextlib import contextmanager 142 | 143 | @contextmanager 144 | def open_file(name): 145 | f = open(name, 'w') 146 | yield f 147 | f.close() 148 | ``` 149 | 150 | Отлично! Реализация менеджера контекста таким способом смотрится более 151 | интуитивной и простой. Тем не менее, этот метод требует определённых 152 | знаний о генераторах, `yield` и декораторах. В примере выше мы не 153 | обрабатываем возможные исключения. В целом, он почти такой же, что и 154 | предыдущий. 155 | 156 | Давайте чуть подробнее разберем этот подход: 157 | 158 | 1. Python встречает ключевое слово `yield`. Благодаря этому он создает 159 | генератор, а не простую функцию. 160 | 2. Благодаря декоратору, `contextmanager` вызывается с функцией 161 | `open_file` в качестве аргумента. 162 | 3. Функция `contextmanager` возвращает генератор, обёрнутый в объект 163 | `GeneratorContextManager`. 164 | 4. `GeneratorContextManager` присваивается функции `open_file`. Таким 165 | образом, когда мы вызовем функцию `open_file` в следующий раз, то 166 | фактически обратимся к объекту `GeneratorContextManager`. 167 | 168 | Теперь, когда мы знаем всё это, мы можем использовать созданный менеджер 169 | контекста следующим образом: 170 | 171 | ```python 172 | with open_file('some_file') as f: 173 | f.write('hola!') 174 | ``` 175 | -------------------------------------------------------------------------------- /book/coroutines.md: -------------------------------------------------------------------------------- 1 | # Корутины 2 | 3 | Корутины похожи на генераторы за исключением нескольких отличий, основные из 4 | которых: 5 | 6 | - генераторы возвращают данные 7 | - корутины потребляют данные 8 | 9 | Для начала рассмотрим процесс создания генератора. Мы можем создать один 10 | таким образом: 11 | 12 | ```python 13 | def fib(): 14 | a, b = 0, 1 15 | while True: 16 | yield a 17 | a, b = b, a + b 18 | ``` 19 | 20 | Такой генератор зачастую используется в цикле `for`: 21 | 22 | ```python 23 | for i in fib(): 24 | print(i) 25 | ``` 26 | 27 | Такой подход отличается скоростью и отсутствием повышенной нагрузки на память, 28 | поскольку значения **генерируются** "на лету" и не хранятся в списке. Теперь, 29 | если мы используем `yield` в примере выше, то, условно говоря, получим 30 | корутину. Корутины потребляют данные, которые им передаются. Вот простой пример 31 | реализации `grep` на Python: 32 | 33 | ```python 34 | def grep(pattern): 35 | print("Searching for", pattern) 36 | while True: 37 | line = (yield) 38 | if pattern in line: 39 | print(line) 40 | ``` 41 | 42 | Подождите! Что возвращает `yield`? Ну, мы преобразовали её в корутину. 43 | Сначала она не содержит значения, вместо этого мы передаём значение из внешнего 44 | источника. Мы передаем значения используя метод `.send()`. Вот простой 45 | пример: 46 | 47 | ```python 48 | search = grep('coroutine') 49 | next(search) 50 | # Вывод: Searching for coroutine 51 | search.send("I love you") 52 | search.send("Don't you love me?") 53 | search.send("I love coroutines instead!") 54 | # Вывод: I love coroutines instead! 55 | ``` 56 | 57 | Передаваемые значения используются в `yield`. Почему мы вызвали `next()`? 58 | Это требуется для запуска корутины. Так же как и в случае с генераторами, 59 | корутины не запускают функцию сразу же. Вместо этого они запускают её в ответ 60 | на вызов методов `__next__()` и `.send()`. По этой причине вам требуется 61 | вызвать `next()`, чтобы исполнение дошло до `yield`. 62 | 63 | Мы можем закрыть корутину при помощи метода `.close()`: 64 | 65 | ```python 66 | search = grep('coroutine') 67 | # ... 68 | search.close() 69 | ``` 70 | 71 | Это лишь основы работы с корутинами. Рекомендую дополнительно просмотреть 72 | эту шикарную [презентацию](http://www.dabeaz.com/coroutines/Coroutines.pdf) 73 | от David Beazley. 74 | -------------------------------------------------------------------------------- /book/debugging.md: -------------------------------------------------------------------------------- 1 | # Отладка 2 | 3 | Отладка относится к числу навыков, овладев которыми, вы значительно продвинете 4 | свои навыки отслеживания багов в коде. Большинство новичков пренебрежительно 5 | относятся к важности отладчика Python (`pdb`). В данной главе я расскажу 6 | лишь о нескольких важных командах. Узнать больше вы можете из официальной 7 | документации. 8 | 9 | ## Запуск из командной строки 10 | 11 | Вы можете запустить скрипт из командной строки вместе с отладчиком. Пример: 12 | 13 | ```bash 14 | $ python -m pdb my_script.py 15 | ``` 16 | 17 | Отладчик приостановит выполнение программы на первой найденной им инструкции. 18 | Это удобно для коротких скриптов. Вы можете проверить значения переменных и 19 | продолжить выполнение программы построчно. 20 | 21 | ## Запуск из скрипта 22 | 23 | Вы можете задать контрольные точки в коде, что позволит изучить значения 24 | переменных и другие параметры в конкретный момент выполнения программы. Это 25 | возможно при помощи метода `pdb.set_trace()`. Вот живой пример: 26 | 27 | ```python 28 | import pdb 29 | 30 | def make_bread(): 31 | pdb.set_trace() 32 | return "У меня нет времени" 33 | 34 | print(make_bread()) 35 | ``` 36 | 37 | Попробуйте запустить этот код. Отладчик откроется сразу после запуска скрипта. 38 | Теперь пришло время познакомиться с несколькими командами отладчика. 39 | 40 | ## Команды 41 | 42 | - `c`: продолжить выполнения программы 43 | - `w`: отобразить окружение текущей исполняемой инструкции 44 | - `a`: отобразить список аргументов текущей функции 45 | - `s`: исполнить текущую строчку кода и остановиться по возможности 46 | - `n`: продолжить исполнение программы пока не будет достигнута следующая 47 | строка текущей функции или пока функция не завершит свою работу 48 | 49 | Разница между `n` и `s` в том, что вторая команда приостановит исполнение 50 | после вызова функции, а первая только после достижения следующей строки текущей 51 | функции. 52 | 53 | Это лишь несколько базовых команд. `pdb` также поддерживает проведение 54 | анализа после завершения работы программы. Это также очень удобная возможность. 55 | Я настоятельно советую вам ознакомиться с официальной документацией и изучить 56 | этот инструмент подробнее. 57 | -------------------------------------------------------------------------------- /book/decorators.md: -------------------------------------------------------------------------------- 1 | # Декораторы 2 | 3 | Декораторы - важная часть Python. Если коротко: они являются функциями, которые 4 | изменяют работу других функций. Они помогают делать код короче и более 5 | "питонистичным". Большинство новичков не знает, где их использовать, так что 6 | я расскажу о нескольких случаях, когда декораторы помогут написать лаконичный 7 | код. 8 | 9 | Для начала рассмотрим, как написать свой собственный декоратор. 10 | 11 | Это будет самым сложным моментом в теме, поэтому мы будем продвигаться шаг за 12 | шагом, так что вы сможете все полностью понять. 13 | 14 | ## Все в Python является объектом 15 | 16 | Для начала краткая ретроспектива функций в Python: 17 | 18 | ```python 19 | def hi(name="yasoob"): 20 | return "Привет " + name 21 | 22 | print(hi()) 23 | # Вывод: 'Привет yasoob' 24 | 25 | # Мы можем присвоить функцию переменной: 26 | greet = hi 27 | # Мы не используем здесь скобки, поскольку наша задача не вызвать функцию, 28 | # а передать её объект переменной. Теперь попробуем запустить 29 | 30 | print(greet()) 31 | # Вывод: 'Привет yasoob' 32 | 33 | # Посмотрим, что произойдет, если мы удалим ссылку на оригинальную функцию 34 | del hi 35 | print(hi()) 36 | # Вывод: NameError 37 | 38 | print(greet()) 39 | # Вывод: 'Привет yasoob' 40 | ``` 41 | 42 | ## Определение функций внутри функций 43 | 44 | Итак, это были основы работы с функциями. Теперь продвинемся на шаг дальше. 45 | В Python разрешено объявлять функции внутри других функций: 46 | 47 | ```python 48 | def hi(name="yasoob"): 49 | print("Вы внутри функции hi()") 50 | 51 | def greet(): 52 | return "Вы внутри функции greet()" 53 | 54 | def welcome(): 55 | return "Вы внутри функции welcome()" 56 | 57 | print(greet()) 58 | print(welcome()) 59 | print("Вы внутри функции hi()") 60 | 61 | hi() 62 | # Вывод: Вы внутри функции hi() 63 | # Вы внутри функции greet() 64 | # Вы внутри функции welcome() 65 | # Вы внутри функции hi() 66 | 67 | # Пример демонстрирует, что при вызове hi() вызываются также функции 68 | # greet() и welcome(). Кроме того, две последние функции недоступны 69 | # извне hi(): 70 | 71 | greet() 72 | # Вывод: NameError: name 'greet' is not defined 73 | ``` 74 | 75 | Теперь мы знаем, что возможно определять функции внутри других функций. Другими 76 | словами: мы можем создавать вложенные функции. Теперь вам нужно познакомиться 77 | с еще одной возможностью функций: возвращать другие функции. 78 | 79 | ## Возвращение функции из функции 80 | 81 | Нам не обязательно исполнять функцию, определенную внутри другой функции сразу, 82 | мы можем вернуть её в качестве возвращаемого значения: 83 | 84 | ```python 85 | def hi(name="yasoob"): 86 | def greet(): 87 | return "Вы внутри функции greet()" 88 | 89 | def welcome(): 90 | return "Вы внутри функции welcome()" 91 | 92 | if name == "yasoob": 93 | return greet 94 | else: 95 | return welcome 96 | 97 | a = hi() 98 | print(a) 99 | # Вывод: 100 | 101 | # Это наглядно демонстрирует, что переменная `a` теперь указывает на 102 | # функцию greet() в функции hi(). Теперь попробуйте вот это 103 | 104 | print(a()) 105 | # Вывод: Вы внутри функции greet() 106 | ``` 107 | 108 | Давайте еще раз пробежимся по коду. Через условный оператор мы возвращаем из 109 | функции объекты `greet` и `welcome`, а не `greet()` и `welcome()`. 110 | Почему? Потому что скобки означают вызов функции, без них мы просто передаем сам 111 | объект функции. Достаточно ясно? Давайте я чуть подробнее остановлюсь на 112 | этом. Когда мы пишем `a = hi()`, функция `hi()` исполняется и (поскольку 113 | имя по умолчанию yasoob) возвращается функция ``greet``. Если мы изменим код 114 | на `a = hi(name="ali")`, то будет возвращена функция `welcome`. Мы также 115 | можем набрать `hi()()`, что вернет `Вы внутри функции greet()`. 116 | 117 | Передаем функцию в качестве аргумента другой функции 118 | 119 | ```python 120 | def hi(): 121 | return "Привет yasoob!" 122 | 123 | def doSomethingBeforeHi(func): 124 | print("Я делаю что-то скучное перед исполнением hi()") 125 | print(func()) 126 | 127 | doSomethingBeforeHi(hi) 128 | # Вывод: Я делаю что-то скучное перед исполнением hi() 129 | # Привет yasoob! 130 | ``` 131 | 132 | Теперь у нас есть все необходимые знания для изучения работы декораторов. 133 | Декораторы позволяют нам исполнять определенный код до и после исполнения 134 | конкретной функции. 135 | 136 | ## Пишем наш первый декоратор 137 | 138 | В прошлом примере мы по сути уже написали декоратор! Давайте изменим его и 139 | сделаем немного более полезным: 140 | 141 | ```python 142 | def a_new_decorator(a_func): 143 | 144 | def wrapTheFunction(): 145 | print("Я делаю что-то скучное перед исполнением a_func()") 146 | 147 | a_func() 148 | 149 | print("Я делаю что-то скучное после исполнения a_func()") 150 | 151 | return wrapTheFunction 152 | 153 | def a_function_requiring_decoration(): 154 | print("Я функция, которая требует декорации") 155 | 156 | a_function_requiring_decoration() 157 | # Вывод: "Я функция, которая требует декорации" 158 | 159 | a_function_requiring_decoration = a_new_decorator(a_function_requiring_decoration) 160 | # Теперь функция a_function_requiring_decoration обернута в wrapTheFunction() 161 | 162 | a_function_requiring_decoration() 163 | # Вывод: Я делаю что-то скучное перед исполнением a_func() 164 | # Я функция, которая требует декорации 165 | # Я делаю что-то скучное после исполнения a_func() 166 | ``` 167 | 168 | Все ясно? Мы просто использовали принципы, с которыми познакомились выше. 169 | Это то, чем и занимаются декораторы в Python! Они "обертывают" функцию и 170 | модифицируют её поведение определенным образом. Сейчас вы можете спросить, 171 | почему мы не используем в коде символ @. Это просто более короткий способ 172 | декорировать функции. Вот как мы можем модифицировать пример выше с 173 | использованием @: 174 | 175 | ```python 176 | @a_new_decorator 177 | def a_function_requiring_decoration(): 178 | """Эй ты! Задекорируй меня полностью!""" 179 | print("Я функция, которая требует декорации") 180 | 181 | a_function_requiring_decoration() 182 | # Вывод: Я делаю что-то скучное перед исполнением a_func() 183 | # Я функция, которая требует декорации 184 | # Я делаю что-то скучное после исполнения a_func() 185 | 186 | # Выражение @a_new_decorator это сокращенная версия следующего кода: 187 | a_function_requiring_decoration = a_new_decorator(a_function_requiring_decoration) 188 | ``` 189 | 190 | Надеюсь, теперь у вас есть базовое представление о логике работы декораторов 191 | в Python. Однако, у нашего кода есть одна проблема. Если мы исполним: 192 | 193 | ```python 194 | print(a_function_requiring_decoration.__name__) 195 | # Вывод: wrapTheFunction 196 | ``` 197 | 198 | Мы этого не ожидали! Имя функции должно быть 199 | `a_function_requiring_decoration`. В реальности наша функция была 200 | заменена на wrapTheFunction. Она перезаписала имя и строку документации 201 | оригинальной функции. К счастью, Python предоставляет нам простой инструмент 202 | для обхода этой проблемы - `functools.wraps`. Давайте исправим 203 | предыдущий пример, используя `functools.wraps`: 204 | 205 | ```python 206 | from functools import wraps 207 | 208 | def a_new_decorator(a_func): 209 | @wraps(a_func) 210 | def wrapTheFunction(): 211 | print("Я делаю что-то скучное перед исполнением a_func()") 212 | a_func() 213 | print("Я делаю что-то скучное после исполнения a_func()") 214 | return wrapTheFunction 215 | 216 | @a_new_decorator 217 | def a_function_requiring_decoration(): 218 | """Эй ты! Задекорируй меня полностью!""" 219 | print("Я функция, которая требует декорации") 220 | 221 | print(a_function_requiring_decoration.__name__) 222 | # Вывод: a_function_requiring_decoration 223 | ``` 224 | 225 | Так намного лучше. Давайте двигаться дальше и знакомиться с конкретными 226 | вариантами использования декораторов. 227 | 228 | **Макет:** 229 | 230 | ```python 231 | from functools import wraps 232 | def decorator_name(f): 233 | @wraps(f) 234 | def decorated(*args, **kwargs): 235 | if not can_run: 236 | return "Функция не будет исполнена" 237 | return f(*args, **kwargs) 238 | return decorated 239 | 240 | @decorator_name 241 | def func(): 242 | return("Функция исполняется") 243 | 244 | can_run = True 245 | print(func()) 246 | # Вывод: Функция исполняется 247 | 248 | can_run = False 249 | print(func()) 250 | # Вывод: Функция не будет исполнена 251 | ``` 252 | 253 | Примечание: `@wraps` принимает на вход функцию для декорирования и 254 | добавляет функциональность копирования имени, строки документации, списка 255 | аргументов и т.д. Это открывает доступ к свойствам декорируемой функции из 256 | декоратора. 257 | 258 | ## Варианты использования 259 | 260 | Теперь давайте рассмотрим области, где декораторы действительно показывают 261 | себя и существенно упрощают работу. 262 | 263 | ### Авторизация 264 | 265 | Декораторы могут использоваться в веб-приложениях для проверки авторизации 266 | пользователя перед тем, как открывать ему доступ к функционалу. Они активно 267 | используются в веб-фреймворках Flask и Django. Вот пример проверки авторизации 268 | на декораторах: 269 | 270 | **Пример:** 271 | 272 | ```python 273 | from functools import wraps 274 | 275 | def requires_auth(f): 276 | @wraps(f) 277 | def decorated(*args, **kwargs): 278 | auth = request.authorization 279 | if not auth or not check_auth(auth.username, auth.password): 280 | authenticate() 281 | return f(*args, **kwargs) 282 | return decorated 283 | ``` 284 | 285 | ### Журналирование 286 | 287 | Журналирование - другая область, в которой декораторы находя широкое применение. 288 | Вот пример: 289 | 290 | ```python 291 | from functools import wraps 292 | 293 | def logit(func): 294 | @wraps(func) 295 | def with_logging(*args, **kwargs): 296 | print(func.__name__ + " была исполнена") 297 | return func(*args, **kwargs) 298 | return with_logging 299 | 300 | @logit 301 | def addition_func(x): 302 | """Считаем что-нибудь""" 303 | return x + x 304 | 305 | 306 | result = addition_func(4) 307 | # Вывод: addition_func была исполнена 308 | ``` 309 | 310 | Уверен, вы уже думаете о каком-нибудь хитром использовании декораторов. 311 | 312 | ## Декораторы с аргументами 313 | 314 | Тогда подумайте вот о чем, является ли `@wraps` также декоратором? Но, 315 | `@wraps` же принимает аргумент, как нормальная функция. Тогда почему бы нам 316 | не сделать что-то похожее с нашими декораторами? 317 | 318 | Когда мы используем синтаксис `@my_decorator` мы применяем декорирующую 319 | функцию с декорируемой функцией в качестве параметра. Как вы помните, все в 320 | Python является объектом, в том числе и функции! Помня это, мы можем писать 321 | функции, возвращающие декорирующие функции. 322 | 323 | ## Вложенные декораторы внутри функции 324 | 325 | Давайте вернемся к нашему примеру с журналированием и напишем декоратор, который 326 | позволит нам задавать файл для сохранения логов: 327 | 328 | ```python 329 | from functools import wraps 330 | 331 | def logit(logfile='out.log'): 332 | def logging_decorator(func): 333 | @wraps(func) 334 | def wrapped_function(*args, **kwargs): 335 | log_string = func.__name__ + " была исполнена" 336 | print(log_string) 337 | # Открываем логфайл и записваем данные 338 | with open(logfile, 'a') as opened_file: 339 | # Мы записываем логи в конкретный файл 340 | opened_file.write(log_string + '\n') 341 | return wrapped_function 342 | return logging_decorator 343 | 344 | @logit() 345 | def myfunc1(): 346 | pass 347 | 348 | myfunc1() 349 | # Вывод: myfunc1 была исполнена 350 | # Файл out.log создан и содержит строку выше 351 | 352 | @logit(logfile='func2.log') 353 | def myfunc2(): 354 | pass 355 | 356 | myfunc2() 357 | # Вывод: myfunc2 была исполнена 358 | # Файл func2.log создан и содержит строку выше 359 | ``` 360 | 361 | ## Декораторы из классов 362 | 363 | Теперь наш журналирующий декоратор находится на продакшене, однако, когда 364 | отдельные части приложения являются критичными, мы определенно хотим отзываться на 365 | возникающие ошибки как можно быстрее. Давайте предположим, что иногда мы 366 | просто хотим записывать логи в файл, а иногда мы хотим получать сообщения об 367 | ошибках по email, сохраняя логи в тоже время. Это подходящий случай 368 | для использования наследования, однако, до сих пор мы встречали только 369 | декораторы-функции. 370 | 371 | К счастью, классы также можно использовать для создания декораторов. Давайте 372 | опробуем эту методику: 373 | 374 | ```python 375 | class logit(object): 376 | def __init__(self, logfile='out.log'): 377 | self.logfile = logfile 378 | 379 | def __call__(self, func): 380 | log_string = func.__name__ + " была исполнена" 381 | print(log_string) 382 | # Открываем логфайл и записваем данные 383 | with open(self.logfile, 'a') as opened_file: 384 | # Мы записываем логи в конкретный файл 385 | opened_file.write(log_string + '\n') 386 | # Отправляем сообщение 387 | self.notify() 388 | 389 | def notify(self): 390 | # Только записываем логи 391 | pass 392 | ``` 393 | 394 | Такое решение имеет дополнительно преимущество в краткости, в сравнении с 395 | вложенными функциями, при этом синтаксис декорирования функции остается 396 | прежним: 397 | 398 | ```python 399 | @logit() 400 | def myfunc1(): 401 | pass 402 | ``` 403 | 404 | Теперь давайте возьмем подкласс `logit` и добавим функционал отправки email 405 | (эта тема не будет здесь рассмотрена): 406 | 407 | ```python 408 | class email_logit(logit): 409 | """ 410 | Реализация logit для отправки писем администраторам при вызове 411 | функции 412 | """ 413 | def __init__(self, email='admin@myproject.com', *args, **kwargs): 414 | self.email = email 415 | super(email_logit, self).__init__(*args, **kwargs) 416 | 417 | def notify(self): 418 | # Отправляем письмо в self.email 419 | # Реализация не будет здесь приведена 420 | pass 421 | ``` 422 | 423 | `@email_logit` будет работать также как и `@logit`, при этом отправляя 424 | сообщения на почту администратору помимо журналирования. 425 | -------------------------------------------------------------------------------- /book/enumerate.md: -------------------------------------------------------------------------------- 1 | # Enumerate 2 | 3 | `enumerate` это встроенная в Python функция. Её пользу не передать одной строкой. 4 | В то же время большинство новичков и многие опытные разработчики не знакомы с 5 | ней. Она позволяет нам итерировать по объекту с параллельным автоматическим 6 | счётчиком. Вот пример: 7 | 8 | ```python 9 | for counter, value in enumerate(some_list): 10 | print(counter, value) 11 | ``` 12 | 13 | Это не всё! `enumerate` принимает также необязательный аргумент, с помощью 14 | которого она становится ещё полезнее. 15 | 16 | ```python 17 | my_list = ['apple', 'banana', 'grapes', 'pear'] 18 | for c, value in enumerate(my_list, 1): 19 | print(c, value) 20 | 21 | # Вывод: 22 | # 1 apple 23 | # 2 banana 24 | # 3 grapes 25 | # 4 pear 26 | ``` 27 | 28 | Необязательный аргумент позволяет задавать начальное значение счётчика. Вы 29 | также можете создать список кортежей, содержащих индекс и элемент, используя 30 | список. Пример: 31 | 32 | ```python 33 | my_list = ['apple', 'banana', 'grapes', 'pear'] 34 | counter_list = list(enumerate(my_list, 1)) 35 | print(counter_list) 36 | # Вывод: [(1, 'apple'), (2, 'banana'), (3, 'grapes'), (4, 'pear')] 37 | ``` 38 | -------------------------------------------------------------------------------- /book/exceptions.md: -------------------------------------------------------------------------------- 1 | # Исключения 2 | 3 | Работа с исключениям это отдельное искусство, освоив которое вы получите 4 | инструмент огромного потенциала. Я продемонстрирую несколько методов работы с 5 | исключениями в этой главе. 6 | 7 | В конечном итоге, нас интересует синтаксис `try/except`. Код, который может 8 | вызвать исключение, помещается в `try` блок, обработка исключения - в 9 | `except`. Простой пример: 10 | 11 | ```python 12 | try: 13 | file = open('test.txt', 'rb') 14 | except IOError as e: 15 | print('Было вызвано исключение IOError. {}'.format(e.args[-1])) 16 | ``` 17 | 18 | В примере выше мы перехватываем только исключение `IOError`. Многие новички не 19 | знают, что мы можем обрабатывать несколько исключений. 20 | 21 | ## Обработка множества исключений 22 | 23 | Мы можем использовать три метода обработки множества исключений. Первый 24 | заключается в создании кортежа из всех возможных исключений. Что-то подобное: 25 | 26 | ```python 27 | try: 28 | file = open('test.txt', 'rb') 29 | except (IOError, EOFError) as e: 30 | print("Было вызвано исключение. {}".format(e.args[-1])) 31 | ``` 32 | 33 | Другой методы заключается в обработке каждого исключения в отдельном блоке 34 | `except`. Мы можем иметь неограниченное их число (но не менее одного). 35 | Очередной пример: 36 | 37 | ```python 38 | try: 39 | file = open('test.txt', 'rb') 40 | except EOFError as e: 41 | print("Было вызвано исключение EOFError.") 42 | raise e 43 | except IOError as e: 44 | print("Было вызвано исключение IOError.") 45 | raise e 46 | ``` 47 | 48 | Таким образом, если исключение не перехватывается первым блоком `except`, то 49 | оно может быть обработано следующим, или не быть обработанным вовсе. 50 | Последний метод заключается в перехвате ВСЕХ исключений: 51 | 52 | ```python 53 | try: 54 | file = open('test.txt', 'rb') 55 | except Exception: 56 | # Логирование, если оно вам требуется 57 | raise 58 | ``` 59 | 60 | Это может быть полезно, когда вы не знаете какие исключения могут возникнуть 61 | в вашей программе. 62 | 63 | ## ``finally`` 64 | 65 | Основной код помещается в блок `try`. Дальше идут блоки `except`, которые 66 | исполняются, если в блоке `try` было вызвано определённое исключение. Третьим 67 | типом блоков, следующим за двумя первыми, может быть `finally`. Код в блоке 68 | `finally` будет исполнен вне зависимости от того, вызвал ли код в блоке 69 | `try` исключение или нет. Это может быть полезно для финальной "чистки" 70 | после работы основного скрипта. Вот простой пример: 71 | 72 | ```python 73 | try: 74 | file = open('test.txt', 'rb') 75 | except IOError as e: 76 | print('Было вызвано исключение IOError. {}'.format(e.args[-1])) 77 | finally: 78 | print("Я буду напечатан вне зависимости от исключений в блоке try!") 79 | 80 | # Вывод: Было вызвано исключение IOError. No such file or directory 81 | # Я буду напечатан вне зависимости от исключений в блоке try! 82 | ``` 83 | 84 | ## ``try/else`` 85 | 86 | Иногда мы можем захотеть исполнить определенный код, если исключения 87 | **не было**. Это легко сделать с помощью блока `else`. Вы можете 88 | спросить: почему нам нужен `else`, если мы можем поместить этот код в блок 89 | `try`? Проблема в том, что исключение в этом коде, может быть в свою очередь 90 | поймано `try`, а мы можем этого и не хотеть. В целом, `else` нечасто 91 | используется, и я, честно говоря, редко к нему прибегаю сам. Пример: 92 | 93 | ```python 94 | try: 95 | print('Я уверен, исключений не будет!') 96 | except Exception: 97 | print('Исключение') 98 | else: 99 | # Любой код, который должен быть исполнен, если исключение в блоке 100 | # try не было вызвано, но для которого не должна проводиться 101 | # обработка исключений 102 | print('Я буду исполнен, если в try не будет исключений.' 103 | 'Мои исключения не будут обрабатываться.') 104 | finally: 105 | print('Я буду исполнен в любом случае!') 106 | 107 | # Вывод: Я уверен, исключений не будет! 108 | # Я буду исполнен, если в try не будет исключений. Мои исключения не будут обрабатываться. 109 | # Я буду исполнен в любом случае! 110 | ``` 111 | 112 | Блок `else`, таким образом, исполняется при отсутствии исключений в блоке 113 | `try`. `else` исполняется перед `finally`. 114 | -------------------------------------------------------------------------------- /book/for_-_else.md: -------------------------------------------------------------------------------- 1 | # ``for/else`` 2 | 3 | Циклы - неотъемлемая часть любого языка программирования. В свою очередь 4 | цикл `for` также важная часть Python. Однако, существует несколько вещей, 5 | связанных с `for`, о которых не знают начинающие разработчики. Мы остановимся 6 | в этой главе на паре нюансов. 7 | 8 | Начнём с того, что мы уже знаем. Мы можем использовать циклы `for` 9 | следующим образом: 10 | 11 | ```python 12 | fruits = ['apple', 'banana', 'mango'] 13 | for fruit in fruits: 14 | print(fruit.capitalize()) 15 | 16 | # Вывод: Apple 17 | # Banana 18 | # Mango 19 | ``` 20 | 21 | Это базовая структура цикла `for`. Теперь перейдем к менее известным 22 | особенностям цикла `for` в Python. 23 | 24 | ## ``else`` 25 | 26 | Циклы `for` могут иметь блок `else` и многие не знакомы с этим фактом. 27 | Блок `else` выполняется, когда цикл завершается в нормальном режиме. 28 | Т.е. не был вызван `break`. Это удобная особенность, которая придется весьма 29 | кстати, когда вы поймете где её стоит использовать. Я узнал об этой возможности 30 | далеко не сразу. 31 | 32 | Типичный пример - поиск элемента в коллекции при помощи цикла `for`. Если 33 | элемент найден - мы останавливаем цикл при помощи `break`. Существует два 34 | сценария, при которых может завершиться исполнение цикла. Первый - элемент 35 | найден и вызван `break`. Второй - элемент так и не был найден и цикл 36 | завершился. Мы хотим узнать, какой из этих двух вариантов вызвал 37 | остановку цикла. Типичным решением будет создание переменной-флага и её 38 | проверка после завершения цикла. Другой способ - использование блока `else`. 39 | 40 | Вот решение на `for/else`: 41 | 42 | ```python 43 | for item in container: 44 | if search_something(item): 45 | # Нашли! 46 | process(item) 47 | break 48 | else: 49 | # Ничего не найдено... 50 | not_found_in_container() 51 | ``` 52 | 53 | Сравним с примером из официальной документации: 54 | 55 | ```python 56 | for n in range(2, 10): 57 | for x in range(2, n): 58 | if n % x == 0: 59 | print(n, 'equals', x, '*', n/x) 60 | break 61 | ``` 62 | 63 | Код выше находит целочисленные делители для n. Теперь интересная часть. Мы 64 | можем использовать блок `else` чтобы отслеживать простые числа и выводить их 65 | на экран: 66 | 67 | ```python 68 | for n in range(2, 10): 69 | for x in range(2, n): 70 | if n % x == 0: 71 | print( n, 'equals', x, '*', n/x) 72 | break 73 | else: 74 | # Цикл не нашел целочисленного делителя для n 75 | print(n, 'is a prime number') 76 | ``` 77 | -------------------------------------------------------------------------------- /book/function_caching.md: -------------------------------------------------------------------------------- 1 | # Кэширование функций 2 | 3 | Кэширование функций позволяет кэшировать возвращаемые значения функций в 4 | зависимости от аргументов. Это может помочь сэкономить время при работе 5 | с вводом/выводом на повторяющихся данных. До Python 3.2 мы должны были бы 6 | написать собственную реализацию. В Python 3.2+ появился декоратор `lru_cache`, 7 | который позволяет быстро кэшировать возвращаемые функцией значения. 8 | 9 | Давайте посмотрим, как мы можем воспользоваться кэшированием в Python 3.2+ и 10 | в более старых версиях. 11 | 12 | ## Python 3.2+ 13 | 14 | Реализуем функцию расчета n-ого числа Фибоначчи с использованием `lru_cache`: 15 | 16 | ```python 17 | from functools import lru_cache 18 | 19 | @lru_cache(maxsize=32) 20 | def fib(n): 21 | if n < 2: 22 | return n 23 | return fib(n - 1) + fib(n - 2) 24 | 25 | >>> print([fib(n) for n in range(10)]) 26 | # Вывод: [0, 1, 1, 2, 3, 5, 8, 13, 21, 34] 27 | ``` 28 | 29 | Аргумент `maxsize` сообщает `lru_cache` сколько последних значений 30 | запоминать. 31 | 32 | Мы также можем легко очистить кэш: 33 | 34 | ```python 35 | fib.cache_clear() 36 | ``` 37 | 38 | ## Python 2+ 39 | 40 | В старых версиях языка также есть несколько путей достижения схожего эффекта. 41 | Вы можете сами реализовать любой тип кэширования. Это зависит только от ваших 42 | потребностей. Вот базовое решение: 43 | 44 | ```python 45 | from functools import wraps 46 | 47 | def memoize(function): 48 | memo = {} 49 | @wraps(function) 50 | def wrapper(*args): 51 | try: 52 | return memo[args] 53 | except KeyError: 54 | rv = function(*args) 55 | memo[args] = rv 56 | return rv 57 | return wrapper 58 | 59 | @memoize 60 | def fibonacci(n): 61 | if n < 2: return n 62 | return fibonacci(n - 1) + fibonacci(n - 2) 63 | 64 | fibonacci(25) 65 | ``` 66 | 67 | **Примечание:** `memoize` не сможет кэшировать типы данных, которые нельзя хешировать (словари, списки и т.д.). Только объекты неизменяемых типов подлежат кэшированию. Держите это в уме при использовании декоратора. 68 | 69 | [Отличная статья](https://www.caktusgroup.com/blog/2015/06/08/testing-client-side-applications-django-post-mortem/) 70 | от Caktus Group, в которой они рассказывают историю бага в Django, который 71 | появился из-за `lru_cache`. Рекомендую к ознакомлению. 72 | -------------------------------------------------------------------------------- /book/generators.md: -------------------------------------------------------------------------------- 1 | # Генераторы 2 | 3 | Для начала нам стоит познакомиться с итераторами. Как подсказывает Wiki, итератор — 4 | это интерфейс, предоставляющий доступ к элементам коллекции (массива или 5 | контейнера). Здесь важно отметить, что итератор только предоставляет доступ, но 6 | не выполняет итерацию по ним. Это может звучать довольно запутано, так что 7 | остановимся чуть подробнее. Тему итераторов можно разбить на три части: 8 | 9 | - Итерируемый объект 10 | - Итератор 11 | - Итерация 12 | 13 | Все эти три части связаны друг с другом. Мы обсудим их одну за одной и после 14 | перейдем к генераторам. 15 | 16 | ## Итерируемый объект 17 | 18 | **Итерируемым объектом** в Python называется любой объект, имеющий методы `__iter__` или `__getitem__`, которые возвращают **итераторы** или могут принимать индексы (подробности [здесь][1]). В итоге итерируемый объект это объект, который может предоставить нам **итератор**. Так что же представляет из себя **итератор**? 19 | 20 | ## Итератор 21 | 22 | Итератором в Python называется объект, который имеет метод `next` (Python 2) 23 | или `__next__`. Вот и все. Это итератор. Теперь об **итерации**. 24 | 25 | ## Итерация 26 | 27 | Если коротко - это процесс получения элементов из какого-нибудь источника, 28 | например списка. Итерация - это процесс перебора элементов объекта в цикле. 29 | Теперь, когда у нас есть общее понимание основных принципов, перейдём к 30 | **генераторам**. 31 | 32 | ## Генераторы 33 | 34 | Генераторы это итераторы, по которым можно итерировать только один раз. Так 35 | происходит поскольку они не хранят все свои значения в памяти, а генерируют 36 | элементы "на лету". Генераторы можно использовать с циклом `for` или любой другой 37 | функцией или конструкцией, которые позволяют итерировать по объекту. В 38 | большинстве случаев **генераторы** создаются как функции. Тем не менее, они не 39 | возвращают значение также как функции (т.е. через `return`), в генераторах 40 | для этого используется ключевое слово `yield`. Вот простой пример 41 | функции-генератора: 42 | 43 | ```python 44 | def generator_function(): 45 | for i in range(10): 46 | yield i 47 | 48 | for item in generator_function(): 49 | print(item) 50 | 51 | # Вывод: 0 52 | # 1 53 | # 2 54 | # 3 55 | # 4 56 | # 5 57 | # 6 58 | # 7 59 | # 8 60 | # 9 61 | ``` 62 | 63 | Не самый полезный код, однако достаточно наглядный. Генераторы хорошо подходят 64 | для расчета больших наборов результатов (при использовании вложенных циклов), где 65 | вам бы не хотелось выделять память для хранения всех результатов одновременно. 66 | Многие функции из стандартной библиотеки, возвращающие `списки` в Python 2, 67 | были модифицированы, для того, чтобы возвращать `генераторы` в Python 3, 68 | поскольку последние требуют меньше ресурсов. 69 | 70 | Вот пример `генератора`, который считает числа Фибоначчи: 71 | 72 | ```python 73 | # generator version 74 | def fibon(n): 75 | a = b = 1 76 | for i in range(n): 77 | yield a 78 | a, b = b, a + b 79 | ``` 80 | 81 | А вот так мы можем его использовать: 82 | 83 | ```python 84 | for x in fibon(1000000): 85 | print(x) 86 | ``` 87 | 88 | С помощью такого метода мы можем не волноваться об использовании большого 89 | объема ресурсов. В то же время следующая реализация алгоритма: 90 | 91 | ```python 92 | def fibon(n): 93 | a = b = 1 94 | result = [] 95 | for i in range(n): 96 | result.append(a) 97 | a, b = b, a + b 98 | return result 99 | ``` 100 | 101 | будет использовать огромный объем наших ресурсов при расчете достаточно больших 102 | чисел. Я уже говорил, что мы можем итерировать по **генераторам** только один 103 | раз, но давайте проверим это на практике. Перед этим вам надо познакомиться с 104 | одной встроенной в язык функцией - `next()`. Она позволяет нам переходить к 105 | следующему элементу коллекции. Давайте проверим наше понимание: 106 | 107 | ```python 108 | def generator_function(): 109 | for i in range(3): 110 | yield i 111 | 112 | gen = generator_function() 113 | print(next(gen)) 114 | # Вывод: 0 115 | print(next(gen)) 116 | # Вывод: 1 117 | print(next(gen)) 118 | # Вывод: 2 119 | print(next(gen)) 120 | # Вывод: Traceback (most recent call last): 121 | # File "", line 1, in 122 | # StopIteration 123 | ``` 124 | 125 | Как видно, после прохождения по всем значениям `next()` начала вызывать 126 | исключение `StopIteration`. По сути, эта ошибка информирует нас о том, что все 127 | значения коллекции уже были пройдены. Может возникнуть вопрос, почему мы не 128 | получаем ошибку при использовании цикла `for`. И ответ довольно прост. Цикл 129 | `for` автоматически перехватывает данное исключение и перестает вызывать 130 | `next`. Знали ли вы, что несколько встроенных типов данных в Python 131 | поддерживают итерирование? Давайте посмотрим: 132 | 133 | ```python 134 | my_string = "Yasoob" 135 | next(my_string) 136 | # Вывод: Traceback (most recent call last): 137 | # File "", line 1, in 138 | # TypeError: str object is not an iterator 139 | ``` 140 | 141 | Ок, это не то что ожидалось. Ошибка говорит, что `str` не итератор. И это 142 | действительно так! Строка - итерируемый объект, но не итератор. Т.е. она 143 | поддерживает итерирование, но мы не можем делать это напрямую. Так как же 144 | нам в конечном итоге итерировать по строке? Пришло время для очередной 145 | встроенной функции - `iter`. Она возвращает **итератор** из **итерируемого** 146 | объекта. `int` не является итерируемым объектом, однако мы можем использовать 147 | `iter` со строками! 148 | 149 | ```python 150 | int_var = 1779 151 | iter(int_var) 152 | # Вывод: Traceback (most recent call last): 153 | # File "", line 1, in 154 | # TypeError: 'int' object is not iterable 155 | # int не итерируемый объект 156 | 157 | my_string = "Yasoob" 158 | my_iter = iter(my_string) 159 | print(next(my_iter)) 160 | # Вывод: 'Y' 161 | ``` 162 | 163 | Теперь намного лучше. Я уверен, что вас заинтересовала эта тема. 164 | Помните, что полностью изучить генераторы можно только через постоянную практику. 165 | Просто используйте **генераторы** везде, где это кажется удачным решением. 166 | Вы не разочаруетесь! 167 | 168 | [1]: https://stackoverflow.com/questions/20551042/whats-the-difference-between-iter-and-getitem/20551346#20551346 169 | -------------------------------------------------------------------------------- /book/global_&_return.md: -------------------------------------------------------------------------------- 1 | # Global и Return 2 | 3 | Вы, вероятно, встречали функции с ключевым словом `return` в конце. Знаете 4 | что оно означает? В целом то же самое что и в других языках. Давайте 5 | посмотрим на эту маленькую функцию: 6 | 7 | ```python 8 | def add(value1, value2): 9 | return value1 + value2 10 | 11 | result = add(3, 5) 12 | print(result) 13 | # Вывод: 8 14 | ``` 15 | 16 | Функция выше принимает два значения и возвращает их сумму. Мы также можем 17 | переписать её таким образом: 18 | 19 | ```python 20 | def add(value1,value2): 21 | global result 22 | result = value1 + value2 23 | 24 | add(3, 5) 25 | print(result) 26 | # Вывод: 8 27 | ``` 28 | 29 | Как несложно заметить, мы используем глобальную переменную `result`. Что это 30 | означает? К глобальным переменным можно обращаться в том числе и извне области 31 | видимости функции. Позвольте продемонстрировать это следующим примером: 32 | 33 | ```python 34 | # Сначала без глобальной переменной 35 | def add(value1, value2): 36 | result = value1 + value2 37 | 38 | add(2, 4) 39 | print(result) 40 | 41 | # Вот чёрт, мы наткнулись на исключение. Но почему? 42 | # Интерпретатор Python говорит нам, что не существует 43 | # переменной с именем result. Так произошло, поскольку 44 | # переменная result доступна исключительно из функции, 45 | # где она была определена, если переменная не является 46 | # глобальной. 47 | Traceback (most recent call last): 48 | File "", line 1, in 49 | result 50 | NameError: name 'result' is not defined 51 | 52 | # Попробуем исправить код, сделав result глобальной 53 | # переменной 54 | def add(value1, value2): 55 | global result 56 | result = value1 + value2 57 | 58 | add(2, 4) 59 | print(result) 60 | # Вывод: 6 61 | ``` 62 | 63 | Во второй раз ошибок быть не должно. В реальной жизни от глобальных переменных 64 | стоит держаться подальше, они только усложняют жизнь, захламляя глобальную 65 | область видимости. 66 | 67 | ## Возврат нескольких значений 68 | 69 | Что если мы хотим вернуть две переменные из функции вместо одной? Есть 70 | несколько способов, которыми пользуются начинающие разработчики. Первый из 71 | них - это использование глобальных переменных. Вот подходящий пример: 72 | 73 | ```python 74 | def profile(): 75 | global name 76 | global age 77 | name = "Danny" 78 | age = 30 79 | 80 | profile() 81 | print(name) 82 | # Вывод: Danny 83 | 84 | print(age) 85 | # Вывод: 30 86 | ``` 87 | 88 | > **Примечание:** как я уже писал, данный метод использовать не рекомендуется. 89 | Повторяю, не используйте вышеуказанный метод! 90 | 91 | Другим популярным методом является возврат кортежа, списка или словаря с 92 | требуемыми значениями. 93 | 94 | ```python 95 | def profile(): 96 | name = "Danny" 97 | age = 30 98 | return (name, age) 99 | 100 | profile_data = profile() 101 | print(profile_data[0]) 102 | # Вывод: Danny 103 | 104 | print(profile_data[1]) 105 | # Вывод: 30 106 | ``` 107 | 108 | Или общепринятое сокращение: 109 | 110 | ```python 111 | def profile(): 112 | name = "Danny" 113 | age = 30 114 | return name, age 115 | 116 | profile_name, profile_age = profile() 117 | print(profile_name) 118 | # Вывод: Danny 119 | 120 | print(profile_age) 121 | # Вывод: 30 122 | ``` 123 | 124 | Не забывайте - в примере выше возвращается кортеж (несмотря на отсутствие скобок), а не отдельные значения. Если вы хотите пойти на один шаг дальше, то попробуйте использовать [namedtuple](https://docs.python.org/3/library/collections.html#collections.namedtuple). Пример: 125 | 126 | ```python 127 | from collection import namedtuple 128 | 129 | def profile(): 130 | Person = namedtuple('Person', 'name age') 131 | return Person(name="Danny", age=31) 132 | 133 | # Использование как namedtuple 134 | p = profile() 135 | print(p, type(p)) 136 | # Person(name='Danny', age=31) 137 | print(p.name) 138 | # Danny 139 | print(p.age) 140 | # 31 141 | 142 | # Использование как простого кортежа 143 | p = profile() 144 | print(p[0]) 145 | # Danny 146 | print(p[1]) 147 | # 31 148 | 149 | # Немедленная распаковка 150 | name, age = profile() 151 | print(name) 152 | # Danny 153 | print(age) 154 | # 31 155 | ``` 156 | 157 | Вместе с возвращением списка или словаря кортежи являются лучшим подходом к решению проблемы. Не используйте глобальные переменные, если точно не уверены в том, что делаете. `global` может быть неплохим вариантом в отдельных редких случаях, но точно не всегда. 158 | -------------------------------------------------------------------------------- /book/lambdas.md: -------------------------------------------------------------------------------- 1 | # Анонимные функции 2 | 3 | Анонимные функции это однострочные функции, которые используются в случаях, 4 | когда вам не нужно повторно использовать функцию в программе. Они идентичны 5 | обыкновенным функциям и повторяют их поведение. 6 | 7 | ## Образец использования 8 | 9 | ```python 10 | lambda argument: manipulate(argument) 11 | ``` 12 | 13 | ## Пример 14 | 15 | ```python 16 | add = lambda x, y: x + y 17 | 18 | print(add(3, 5)) 19 | # Вывод: 8 20 | ``` 21 | 22 | Вот несколько случаев, где удобно использовать анонимные функции, и где они 23 | часто применяются в реальной жизни: 24 | 25 | ### Сортировка списка 26 | 27 | ```python 28 | a = [(1, 2), (4, 1), (9, 10), (13, -3)] 29 | a.sort(key=lambda x: x[1]) 30 | 31 | print(a) 32 | # Вывод: [(13, -3), (4, 1), (1, 2), (9, 10)] 33 | ``` 34 | 35 | ### Параллельная сортировка списков 36 | 37 | ```python 38 | data = list(zip(list1, list2)) 39 | data.sort() 40 | list1, list2 = map(lambda t: list(t), zip(*data)) 41 | ``` 42 | -------------------------------------------------------------------------------- /book/map_filter.md: -------------------------------------------------------------------------------- 1 | # Map, Filter и Reduce 2 | 3 | В Python есть три функции, которые значительно упрощают функциональный подход 4 | к программированию. Мы обсудим их и рассмотрим примеры использования. 5 | 6 | ## Map 7 | 8 | ``map`` применяет функцию ко всем элементам списка. Если коротко: 9 | 10 | ### Сценарий использования 11 | 12 | ```python 13 | map(function_to_apply, list_of_inputs) 14 | ``` 15 | 16 | Нам часто необходимо передать все элементы списка в функцию один за другим и 17 | собрать возвращаемые значения в новый список. К примеру: 18 | 19 | ```python 20 | items = [1, 2, 3, 4, 5] 21 | squared = [] 22 | for i in items: 23 | squared.append(i**2) 24 | ``` 25 | 26 | `map` позволяет выполнить эту задачу элегантным способом: 27 | 28 | ```python 29 | items = [1, 2, 3, 4, 5] 30 | squared = list(map(lambda x: x**2, items)) 31 | ``` 32 | 33 | Зачастую с `map` используются анонимные функции, как в примере выше. Вместо 34 | списка входных данных можно также использовать список функций! 35 | 36 | ```python 37 | def multiply(x): 38 | return (x*x) 39 | def add(x): 40 | return (x+x) 41 | 42 | funcs = [multiply, add] 43 | for i in range(5): 44 | value = list(map(lambda x: x(i), funcs)) 45 | print(value) 46 | 47 | # Вывод: 48 | # [0, 0] 49 | # [1, 2] 50 | # [4, 4] 51 | # [9, 6] 52 | # [16, 8] 53 | ``` 54 | 55 | ## Filter 56 | 57 | Как можно догадаться по имени, `filter` возвращает список элементов, для 58 | которых заданная функция возвращает `True`. Вот простой и понятный пример: 59 | 60 | ```python 61 | number_list = range(-5, 5) 62 | less_than_zero = list(filter(lambda x: x < 0, number_list)) 63 | print(less_than_zero) 64 | 65 | # Вывод: [-5, -4, -3, -2, -1] 66 | ``` 67 | 68 | `filer` уподобляется циклу, но он является встроенной функцией и работает 69 | быстрее. 70 | 71 | > **Примечание:** Если `map` и `filter` не кажутся вам достаточно красивым 72 | решением, то вы всегда можете использовать абстракции списков/словарей/кортежей. 73 | Использование последних считается хорошим тоном, так как практически во всех случаях 74 | улучшает читаемость без потери функционала. 75 | 76 | ## Reduce 77 | 78 | `Reduce` весьма полезная функция для выполнения вычислений на списке и возвращения единственного результата. Она сворачивает список, применяя полученную в качестве аргумента функцию по очереди к последовательным парам элементов. Например, если мы хотим посчитать произведение всех элементов списка чисел. 79 | 80 | Обычным решением этой задачи будет использования цикла `for`: 81 | 82 | ```python 83 | product = 1 84 | list = [1, 2, 3, 4] 85 | for num in list: 86 | product = product * num 87 | 88 | # product = 24 89 | ``` 90 | 91 | Теперь попробуем с `reduce`: 92 | 93 | ```python 94 | from functools import reduce 95 | product = reduce((lambda x, y: x * y), [1, 2, 3, 4]) 96 | 97 | # Вывод: 24 98 | ``` 99 | -------------------------------------------------------------------------------- /book/mutation.md: -------------------------------------------------------------------------------- 1 | # Изменяемость 2 | 3 | Изменяемые и неизменяемые типы данных в Python традиционно являются причиной 4 | головной боли у начинающих разработчиков. Как следует из названия изменяемые 5 | объекты *можно модифицировать*, неизменяемые - *постоянны*. Заставим голову 6 | кружиться? Смотрим пример: 7 | 8 | ```python 9 | foo = ['hi'] 10 | print(foo) 11 | # Вывод: ['hi'] 12 | 13 | bar = foo 14 | bar += ['bye'] 15 | print(foo) 16 | # Вывод: ['hi', 'bye'] 17 | ``` 18 | 19 | Что произошло? Мы этого не ожидали! Логично было бы увидеть: 20 | 21 | ```python 22 | foo = ['hi'] 23 | print(foo) 24 | # Вывод: ['hi'] 25 | 26 | bar = foo 27 | bar += ['bye'] 28 | 29 | print(foo) 30 | # Ожидаемый вывод: ['hi'] 31 | # Вывод: ['hi', 'bye'] 32 | 33 | print(bar) 34 | # Вывод: ['hi', 'bye'] 35 | ``` 36 | 37 | Это не баг, а изменяемые типы данных в действии. Каждый раз, когда вы 38 | присваиваете значение переменной изменяемого типа другой переменной, все 39 | изменения с этим значением будут отражаться на обоих переменных. Новая 40 | переменная становится ссылкой на старую. Так происходит только с изменяемыми 41 | типами данных. Вот пример с использованием функций и изменяемых типов данных: 42 | 43 | ```python 44 | def add_to(num, target=[]): 45 | target.append(num) 46 | return target 47 | 48 | add_to(1) 49 | # Вывод: [1] 50 | 51 | add_to(2) 52 | # Вывод: [1, 2] 53 | 54 | add_to(3) 55 | # Вывод: [1, 2, 3] 56 | ``` 57 | 58 | Вы могли ожидать другого поведения. Например, что функция `add_to` будет 59 | возвращать новый список при каждом вызове: 60 | 61 | ```python 62 | def add_to(num, target=[]): 63 | target.append(num) 64 | return target 65 | 66 | add_to(1) 67 | # Вывод: [1] 68 | 69 | add_to(2) 70 | # Вывод: [2] 71 | 72 | add_to(3) 73 | # Вывод: [3] 74 | ``` 75 | 76 | Причиной, опять же, является изменяемость списков, которая и вызывает основную 77 | боль. В Python аргументы функции обрабатываются при определении функции, а не 78 | при её вызове. По этой причине вы никогда не должны присваивать аргументам по 79 | умолчанию значения изменяемых типов, если абсолютно точно не уверены в своих 80 | действиях конечно. Правильным подходом будет такой код: 81 | 82 | ```python 83 | def add_to(element, target=None): 84 | if target is None: 85 | target = [] 86 | target.append(element) 87 | return target 88 | ``` 89 | 90 | Каждый раз при вызове функции без аргумента `target` будет создан новый 91 | список. К примеру: 92 | 93 | ```python 94 | add_to(42) 95 | # Вывод: [42] 96 | 97 | add_to(42) 98 | # Вывод: [42] 99 | 100 | add_to(42) 101 | # Вывод: [42] 102 | ``` 103 | -------------------------------------------------------------------------------- /book/object_introspection.md: -------------------------------------------------------------------------------- 1 | # Анализ объекта 2 | 3 | В программировании, под анализом объекта понимается возможность определения его 4 | типа во время исполнения программы. Это одна из сильных сторон Python. 5 | Все в Python является объектами и мы можем их исследовать. В языке есть 6 | несколько встроенных функций и модулей для этой цели. 7 | 8 | ## `dir` 9 | 10 | В этом параграфе мы познакомимся с функцией `dir` и как она помогает нам в 11 | анализе объектов. 12 | 13 | Это одна из важнейших функций для этой задачи. Она возвращает список атрибутов 14 | и методов объекта. Вот пример: 15 | 16 | ```python 17 | my_list = [1, 2, 3] 18 | dir(my_list) 19 | # Вывод: ['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', 20 | # '__delslice__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', 21 | # '__getitem__', '__getslice__', '__gt__', '__hash__', '__iadd__', '__imul__', 22 | # '__init__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', 23 | # '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', 24 | # '__setattr__', '__setitem__', '__setslice__', '__sizeof__', '__str__', 25 | # '__subclasshook__', 'append', 'count', 'extend', 'index', 'insert', 'pop', 26 | # 'remove', 'reverse', 'sort'] 27 | ``` 28 | 29 | Мы получили все методы списка. Это может быть полезно, если вы не 30 | можете вспомнить имя конкретного метода. Если мы запустим `dir()` без 31 | аргументов, то она вернет нам имена всех объектов в текущей области видимости. 32 | 33 | ## `type` и `id` 34 | 35 | Функция `type` возвращает тип объекта. Пример: 36 | 37 | ```python 38 | print(type('')) 39 | # Вывод: 40 | 41 | print(type([])) 42 | # Вывод: 43 | 44 | print(type({})) 45 | # Вывод: 46 | 47 | print(type(dict)) 48 | # Вывод: 49 | 50 | print(type(3)) 51 | # Вывод: 52 | ``` 53 | 54 | `id` возвращает уникальный идентификатор объекта. К примеру: 55 | 56 | ```python 57 | name = "Yasoob" 58 | print(id(name)) 59 | # Вывод: 139972439030304 60 | ``` 61 | 62 | ## Модуль `inspect` 63 | 64 | Модуль `inspect` также предоставляет несколько полезных функций для получения 65 | информации об объектах. Например, вы можете проверить члены объекта, запустив: 66 | 67 | ```python 68 | import inspect 69 | print(inspect.getmembers(str)) 70 | # Вывод: [('__add__', 36 | 37 | int add_int(int, int); 38 | float add_float(float, float); 39 | 40 | int add_int(int num1, int num2){ 41 | return num1 + num2; 42 | } 43 | 44 | float add_float(float num1, float num2){ 45 | return num1 + num2; 46 | } 47 | ``` 48 | 49 | Теперь скомпилируем C-файл в `.so`-файл (DLL под Windows). Так мы получим 50 | файл `adder.so`. 51 | 52 | ```bash 53 | # Для Linux 54 | $ gcc -shared -Wl,-soname,adder -o adder.so -fPIC add.c 55 | 56 | # Для Mac 57 | $ gcc -shared -Wl,-install_name,adder.so -o adder.so -fPIC add.c 58 | ``` 59 | 60 | Теперь Python-код: 61 | 62 | ```python 63 | from ctypes import * 64 | 65 | # Загружаем библиотеку 66 | adder = CDLL('./adder.so') 67 | 68 | # Находим сумму целых чисел 69 | res_int = adder.add_int(4,5) 70 | print("Сумма 4 и 5 = " + str(res_int)) 71 | 72 | # Находим сумму действительных чисел 73 | a = c_float(5.5) 74 | b = c_float(4.1) 75 | 76 | add_float = adder.add_float 77 | add_float.restype = c_float 78 | print("Сумма 5.5 и 4.1 = " + str(add_float(a, b))) 79 | ``` 80 | 81 | Результат: 82 | 83 | ``` 84 | Сумма 4 и 5 = 9 85 | Сумма 5.5 и 4.1 = 9.60000038147 86 | ``` 87 | 88 | В примере выше, C-файл содержит простой код - две функции: одна для нахождения 89 | суммы двух целых чисел, другая - действительных. 90 | 91 | В Python-коде мы сначала импортируем модуль `ctypes`. Затем функция `CDLL` 92 | из того же модуля используется для загрузки C-библиотеки. Теперь 93 | функции из C-кода доступны для нас через переменную `adder`. Когда мы 94 | вызываем `adder.add_int()`, то автоматически вызывается C-функция 95 | `add_int`. Интерфейс модуля `ctypes` позволяет использовать питоновские 96 | целые числа и строки при вызове C-функций. 97 | 98 | Для других типов, например логического или действительных чисел, мы должны 99 | использовать корректные `ctypes`. Мы делаем это при передаче параметров в 100 | `adder.add_float()`. Сначала мы создаём требуемый тип `c_float` из ``float``, 101 | затем используем в качестве аргумента для C-кода. Этот метод простой и 102 | аккуратный, но ограниченный. Мы, к примеру, не можем оперировать объектами на 103 | стороне C-кода. 104 | 105 | ## SWIG 106 | 107 | Simplified Wrapper and Interface Generator, или SWIG для краткости, это другой 108 | способ работы с C-кодом из Python. В этом методе разработчик должен написать 109 | отдельный файл, описывающий интерфейс, который будет передаваться в SWIG 110 | (утилиту командной строки). 111 | 112 | Python-разработчики обычно не используют данный подход, поскольку в большинстве 113 | случаев он неоправданно сложен. Тем не менее, это отличный вариант, когда у вас 114 | есть C/C++ код, к которому нужно обращаться из множества различных языков. 115 | 116 | **Пример** (с [сайта SWIG](http://www.swig.org/tutorial.html)) 117 | 118 | C-код, `example.c` содержит различные функции и переменные: 119 | 120 | ```c 121 | #include 122 | double My_variable = 3.0; 123 | 124 | int fact(int n) { 125 | if (n <= 1) return 1; 126 | else return n*fact(n-1); 127 | } 128 | 129 | int my_mod(int x, int y) { 130 | return (x%y); 131 | } 132 | 133 | char *get_time() 134 | { 135 | time_t ltime; 136 | time(<ime); 137 | return ctime(<ime); 138 | } 139 | ``` 140 | 141 | Файл, описывающий интерфейс. Он не будет изменяться в зависимости от языка, на 142 | который вы хотите портировать свой C-код: 143 | 144 | ``` 145 | /* example.i */ 146 | %module example 147 | %{ 148 | /* Помещаем сюда заголовочные файлы или объявления функций */ 149 | extern double My_variable; 150 | extern int fact(int n); 151 | extern int my_mod(int x, int y); 152 | extern char *get_time(); 153 | %} 154 | 155 | extern double My_variable; 156 | extern int fact(int n); 157 | extern int my_mod(int x, int y); 158 | extern char *get_time(); 159 | ``` 160 | 161 | Компиляция: 162 | 163 | ``` 164 | unix % swig -python example.i 165 | unix % gcc -c example.c example_wrap.c \ 166 | -I/usr/local/include/python2.1 167 | unix % ld -shared example.o example_wrap.o -o _example.so 168 | ``` 169 | 170 | Python: 171 | 172 | ```python 173 | >>> import example 174 | >>> example.fact(5) 175 | 120 176 | >>> example.my_mod(7,3) 177 | 1 178 | >>> example.get_time() 179 | 'Sun Feb 11 23:01:07 1996' 180 | >>> 181 | ``` 182 | 183 | Как мы можем видеть, SWIG позволяет добиваться нужного нам эффекта, но он требует 184 | дополнительных усилий, которые, однако, стоит затратить, если вас интересует 185 | возможность запуска C-кода из множества различных языков. 186 | 187 | ## Python/C API 188 | 189 | [C/Python API](https://docs.python.org/2/c-api/) это, вероятно, наиболее 190 | широко применяемый метод - не благодаря своей простоте, а потому что он 191 | позволяет оперировать Python объектами из C кода. 192 | 193 | Этот метод подразумевает написание C-кода специально для работы с Python. Все 194 | объекты Python представляются как структуры PyObject и заголовочный файл 195 | `Python.h` предоставляет различные функции для работы с объектами. Например, 196 | если `PyObject` одновременно `PyListType` (список), то мы можем использовать 197 | функцию `PyList_Size()`, чтобы получить длину списка. Это эквивалентно коду 198 | `len(some_list)` в Python. Большинство основных функций/операторов для 199 | стандартных Python объектов доступны в C через `Python.h`. 200 | 201 | **Пример** 202 | 203 | Давайте напишем С-библиотеку для суммирования всех элементов списка Python 204 | (все элементы являются числами). 205 | 206 | Начнем с интерфейса, который мы хотим иметь в итоге. 207 | Вот Python-файл, использующий пока отсутствующую C-библиотеку: 208 | 209 | ```python 210 | # Это не простой Python import, addList это C-библиотека 211 | import addList 212 | 213 | l = [1,2,3,4,5] 214 | print("Сумма элементов списка - " + str(l) + " = " + str(addList.add(l))) 215 | ``` 216 | 217 | Смотрится как обыкновенный Python-код, который импортирует и использует 218 | Python-модуль `addList`. Единственная разница - модуль `addList` написан 219 | на C. 220 | 221 | Дальше на повестке у нас C-код, который будет встроен в Python-модуль 222 | `addList`, это может смотреться немного странно, однако, разобрав отдельные 223 | части, из которых состоит C-файл, вы увидите, что все относительно 224 | незатейливо. 225 | 226 | *adder.c* 227 | 228 | ```c 229 | // Python.h содержит все необходимые функции, для работы с объектами Python 230 | #include 231 | 232 | // Эту функцию мы вызываем из Python кода 233 | static PyObject* addList_add(PyObject* self, PyObject* args){ 234 | 235 | PyObject * listObj; 236 | 237 | // Входящие аргументы находятся в кортеже 238 | // В нашем случае есть только один аргумент - список, на который мы будем 239 | // ссылаться как listObj 240 | if (! PyArg_ParseTuple( args, "O", &listObj)) 241 | return NULL; 242 | 243 | // Длина списка 244 | long length = PyList_Size(listObj); 245 | 246 | // Проходимся по всем элементам 247 | long i, sum =0; 248 | for(i = 0; i < length; i++){ 249 | // Получаем элемент из списка - он также Python-объект 250 | PyObject* temp = PyList_GetItem(listObj, i); 251 | // Мы знаем, что элемент это целое число - приводим его к типу C long 252 | long elem = PyInt_AsLong(temp); 253 | sum += elem; 254 | } 255 | 256 | // Возвращаемое в Python-код значение также Python-объект 257 | // Приводим C long к Python integer 258 | return Py_BuildValue("i", sum); 259 | } 260 | 261 | // Немного документации для `add` 262 | static char addList_docs[] = 263 | "add( ): add all elements of the list\n"; 264 | 265 | /* 266 | Эта таблица содержит необходимую информацию о функциях модуля 267 | <имя функции в модуле Python>, <фактическая функция>, 268 | <ожидаемые типы аргументов функции>, <документация функции> 269 | */ 270 | static PyMethodDef addList_funcs[] = { 271 | {"add", (PyCFunction)addList_add, METH_VARARGS, addList_docs}, 272 | {NULL, NULL, 0, NULL} 273 | }; 274 | 275 | /* 276 | addList имя модуля и это блок его инициализации. 277 | <желаемое имя модуля>, <таблица информации>, <документация модуля> 278 | */ 279 | PyMODINIT_FUNC initaddList(void){ 280 | Py_InitModule3("addList", addList_funcs, 281 | "Add all ze lists"); 282 | } 283 | ``` 284 | 285 | Пошаговое объяснение: 286 | 287 | - Заголовочный файл `` содержит все требуемые типы (для 288 | представления типов объектов в Python) и определения функций (для работы с 289 | Python-объектами). 290 | - Дальше мы пишем функцию, которую собираемся вызывать из Python. По 291 | соглашению, имя функции принимается {module-name}\_{function-name}, которое 292 | в нашем случае - `addList_add`. Подробнее об этой функции будет дальше. 293 | - Затем заполняем таблицу, которая содержит всю необходимую информацию о 294 | функциях, которые мы хотим иметь в модуле. Каждая строка относится к 295 | функции, последняя - контрольное значение (строка из null элементов). 296 | - Затем идёт блок инициализации модуля - `PyMODINIT_FUNC init{module-name}`. 297 | 298 | Функция `addList_add` принимает аргументы типа `PyObject` (args также является 299 | кортежем, но поскольку в Python всё является объектами, мы используем 300 | унифицированный тип `PyObject`). Мы парсим входные аргументы (фактически, 301 | разбиваем кортеж на отдельные элементы) при помощи `PyArg_ParseTuple()`. 302 | Первый параметр является аргументом для парсинга. Второй аргумент - строка, 303 | регламентирующая процесс парсинга элементов кортежа args. Знак на N-ой позиции 304 | строки сообщает нам тип N-ого элемента кортежа args, например - 'i' значит 305 | integer, 's' - строка и 'O' - Python-объект. Затем следует несколько 306 | аргументов, где мы хотели бы хранить выходные элементы `PyArg_ParseTuple()`. 307 | Число этих аргументов равно числу аргументов, которые планируется передавать 308 | в функцию модуля и их позиционность должна соблюдаться. Например, если мы 309 | ожидаем строку, целое число и список в таком порядке, сигнатура функции будет 310 | следующего вида: 311 | 312 | ```c 313 | int n; 314 | char *s; 315 | PyObject* list; 316 | PyArg_ParseTuple(args, "siO", &s, &n, &list); 317 | ``` 318 | 319 | В данном случае, нам нужно извлечь только объект списка и сохранить его в 320 | переменной `listObj`. Затем мы используем функцию `PyList_Size()` чтобы 321 | получить длину списка. Логика совпадает с `len(some_list)` в Python. 322 | 323 | Теперь мы итерируем по списку, получая элементы при помощи функции 324 | `PyLint_GetItem(list, index)`. Так мы получаем PyObject\*. Однако, поскольку 325 | мы знаем, что Python-объекты еще и `PyIntType`, то используем функцию 326 | `PyInt_AsLong(PyObj *)` для получения значения. Выполняем процедуру для 327 | каждого элемента и получаем сумму. 328 | 329 | Сумма преобразуется в Python-объект и возвращается в Python-код при помощи 330 | `Py_BuildValue()`. Аргумент "i" означает, что возвращаемое значение имеет 331 | тип integer. 332 | 333 | В заключение мы собираем C-модуль. Сохраните следующий код как файл 334 | `setup.py`: 335 | 336 | ```python 337 | # Собираем модули 338 | 339 | from distutils.core import setup, Extension 340 | 341 | setup(name='addList', version='1.0',\ 342 | ext_modules=[Extension('addList', ['adder.c'])]) 343 | ``` 344 | 345 | и запустите: 346 | 347 | ```bash 348 | python setup.py install 349 | ``` 350 | 351 | Это соберёт и установит C-файл в Python-модуль, который нам требуется. 352 | 353 | Теперь осталось только протестировать работоспособность: 354 | 355 | ```python 356 | # Модуль, вызывающий C-код 357 | import addList 358 | 359 | l = [1,2,3,4,5] 360 | print("Сумма элементов списка - " + str(l) + " = " + str(addList.add(l))) 361 | ``` 362 | 363 | Результат: 364 | 365 | ``` 366 | Сумма элементов списка - [1, 2, 3, 4, 5] = 15 367 | ``` 368 | 369 | В итоге, как вы можете видеть, мы получили наше первое C-расширение, 370 | использующее Python.h API. Этот метод может показаться сложным, однако с 371 | практикой вы поймёте его удобство. 372 | 373 | Из других методов встраивания C-кода в Python, можно отметить альтернативный и 374 | быстрый компилятор [Cython](http://cython.org/). Однако Cython, по сути, 375 | отличный от основной ветки Python язык, поэтому я не стал здесь его рассматривать. 376 | -------------------------------------------------------------------------------- /book/set_-_data_structure.md: -------------------------------------------------------------------------------- 1 | # Структура данных `set` 2 | 3 | `set` это весьма полезная структура данных. `set` схож по поведению 4 | со списком, за тем исключением, что множества неупорядочены и не могут 5 | содержать одинаковые значения. Это бывает очень кстати в определённых случаях. 6 | К примеру, вам может потребоваться проверить список на наличие дубликатов. 7 | Существует два типичных метода. Первый - использовать цикл `for`. Как-то так: 8 | 9 | ```python 10 | some_list = ['a', 'b', 'c', 'b', 'd', 'm', 'n', 'n'] 11 | 12 | duplicates = [] 13 | for value in some_list: 14 | if some_list.count(value) > 1: 15 | if value not in duplicates: 16 | duplicates.append(value) 17 | 18 | print(duplicates) 19 | # Вывод: ['b', 'n'] 20 | ``` 21 | 22 | Но есть и более простое решение при помощи множества. Вы можете сделать 23 | что-нибудь такое: 24 | 25 | ```python 26 | some_list = ['a', 'b', 'c', 'b', 'd', 'm', 'n', 'n'] 27 | duplicates = set([x for x in some_list if some_list.count(x) > 1]) 28 | print(duplicates) 29 | # Вывод: set(['b', 'n']) 30 | ``` 31 | 32 | У множеств также есть несколько дополнительных методов. Вот некоторые 33 | из них: 34 | 35 | ## Пересечение 36 | 37 | Вы можете найти пересечение двух множества. Например: 38 | 39 | ```python 40 | valid = set(['yellow', 'red', 'blue', 'green', 'black']) 41 | input_set = set(['red', 'brown']) 42 | print(input_set.intersection(valid)) 43 | # Вывод: set(['red']) 44 | ``` 45 | 46 | ## Разность 47 | 48 | Разность двух множеств можно найти следующим методом: 49 | 50 | ```python 51 | valid = set(['yellow', 'red', 'blue', 'green', 'black']) 52 | input_set = set(['red', 'brown']) 53 | print(input_set.difference(valid)) 54 | # Вывод: set(['brown']) 55 | ``` 56 | 57 | Множества также можно создавать с помощью новой нотации: 58 | 59 | ```python 60 | a_set = {'red', 'blue', 'green'} 61 | print(type(a_set)) 62 | # Вывод: 63 | ``` 64 | 65 | Существуют и другими методы. Рекомендую ознакомиться с ними в официальной 66 | документации. 67 | -------------------------------------------------------------------------------- /book/targeting_python_2_3.md: -------------------------------------------------------------------------------- 1 | # Разработка под Python 2+3 2 | 3 | Во многих случаях вам может понадобиться писать программы, которые будут 4 | корректно работать и на Python 2+ и на 3+. 5 | 6 | Представьте, что у вас есть крайне популярный Python-пакет, который используют 7 | тысячи людей, но не у всех есть Python 2 или Python 3. В этом случае у вас есть 8 | два варианта. Первый - разрабатывать две версии параллельно, одну для второй 9 | ветки Python, а другую - для третьей. Другим вариантом будет изменение текущего 10 | кода для совместимости как с Python 2, так и с Python 3. 11 | 12 | В данном разделе я собираюсь рассказать о нескольких приёмах, которые вы 13 | можете использовать, чтобы сделать скрипт совместимым с обоими ветками Python. 14 | 15 | ## Импорты Future 16 | 17 | Первым и наиболее важным методом будет использование импорта `__future__`. 18 | Это позволяет вам импортировать функционал Python 3 в Python 2. Ниже несколько 19 | примеров. 20 | 21 | Менеджеры контекста были нововведением в Python 2.6+. Для их использования в 22 | Python 2.5 вам необходимо: 23 | 24 | ```python 25 | from __future__ import with_statement 26 | ``` 27 | 28 | `print` стал функцией в Python 3. Для её использования в Python 2 вы можете 29 | импортировать функцию из `__future__`: 30 | 31 | ```python 32 | print 33 | # Вывод: 34 | 35 | from __future__ import print_function 36 | print(print) 37 | # Вывод: 38 | ``` 39 | 40 | ## Переименование модулей 41 | 42 | Для начала, скажите мне как вы импортируете модули в ваших скриптах? 43 | Большинство делает так: 44 | 45 | ```python 46 | import foo 47 | # или 48 | from foo import bar 49 | ``` 50 | 51 | А знаете ли вы про такой способ: 52 | 53 | ```python 54 | import foo as foo 55 | ``` 56 | 57 | Я знаю, что эффект будет такой же, как и в первом случае, но этот способ критичен 58 | для совместимости программ с Python 2 и 3. Попробуем следующий код: 59 | 60 | ```python 61 | try: 62 | import urllib.request as urllib_request # для Python 3 63 | except ImportError: 64 | import urllib2 as urllib_request # для Python 2 65 | ``` 66 | 67 | Позвольте мне немного пояснить. Мы используем блок `try/except` для импорта. 68 | Причина - в Python 2 нет модуля `urllib.request`, поэтому попытка его 69 | импорта приведет к `ImportError`. Функциональность `urllib.request` 70 | доступна в модуле `urlib2` в Python 2. Таким образом, при использовании 71 | Python 2, при импорте `urllib.request` мы упираемся в Exception и импортируем 72 | `urllib2` в качестве альтернативы. 73 | 74 | Последнее что вам нужно знать - ключевое слово `as`. С его помощью мы 75 | импортируем модуль под именем `urllib_request`. Дальше в коде мы просто 76 | будем ссылаться на это имя, вне зависимости от того, какую ветку Python мы 77 | используем. 78 | 79 | ## Заменяем устаревшие модули Python 2 80 | 81 | В Python 2 есть 12 устаревших модулей, которые были удалены в Python 3. 82 | Для сохранения совместимости убедитесь, что не используете их в своем коде. 83 | Следующим образом можно явно запретить использование удаленного в Python 3 84 | функционала: 85 | 86 | ```python 87 | from future.builtins.disabled import * 88 | ``` 89 | 90 | Теперь, при использовании удаленного в Python 3 модуля, мы будем получать 91 | `NameError`: 92 | 93 | ```python 94 | from future.builtins.disabled import * 95 | 96 | apply() 97 | # Вывод: NameError: obsolete Python 2 builtin apply is disabled 98 | ``` 99 | 100 | ## Сторонние бэкпорты 101 | 102 | Существует несколько пакетов, которые предоставляют новый функционал Python 3 103 | в Python 2. Например: 104 | 105 | - enum `pip install enum34` 106 | - singledispatch `pip install singledispatch` 107 | - pathlib `pip install pathlib` 108 | 109 | В качестве дополнительного чтения: официальная документация содержит 110 | [исчерпывающее руководство](https://docs.python.org/3/howto/pyporting.html), 111 | объясняющее шаги, которые вам нужно предпринять для гарантии совместимости кода 112 | с обеими ветками Python. 113 | -------------------------------------------------------------------------------- /book/ternary_operators.md: -------------------------------------------------------------------------------- 1 | # Тернарные операторы 2 | 3 | Тернарные операторы наиболее широко известны в Python как условные выражения. 4 | Эти операторы возвращают что-то в зависимости от того, является ли условие 5 | истиной или ложью. Они стали частью языка с версии 2.4. 6 | 7 | Ниже приведены шаблоны и примеры использования. 8 | 9 | **Шаблон:** 10 | 11 | ```python 12 | condition_if_true if condition else condition_if_false 13 | ``` 14 | 15 | **Пример:** 16 | 17 | ```python 18 | is_nice = True 19 | state = "nice" if is_nice else "not nice" 20 | ``` 21 | 22 | Такой подход позволяет быстро проверить условие, а не писать несколько строчек 23 | оператора `if`. Зачастую это очень удобно, поскольку позволяет писать более 24 | компактный код, сохраняя его читабельность. 25 | 26 | Другим вариантом (менее очевидным и не настолько широко распространенным) 27 | является использование кортежей. Вот пример кода: 28 | 29 | **Шаблон:** 30 | 31 | ```python 32 | (if_test_is_false, if_test_is_true)[test] 33 | ``` 34 | 35 | **Пример:** 36 | 37 | ```python 38 | nice = True 39 | personality = ("вредная", "добрая")[nice] 40 | print("Кошка ", personality) 41 | # Вывод: Кошка добрая 42 | ``` 43 | 44 | Это работает поскольку в Python `True == 1` и `False == 0`. Помимо кортежей 45 | можно использовать списки. 46 | 47 | Пример выше редко используется и в основном считается плохой практикой у 48 | разработчиков, поскольку не является в должной мере "питонистичным" решением. 49 | Вдобавок здесь легко ошибиться в последовательности значений в кортеже. 50 | 51 | Другой причиной не пользоваться тернарным оператором на кортежах является 52 | обработка всего кортежа при исполнении, когда как для if-else оператора 53 | такого не происходит. 54 | 55 | **Пример:** 56 | 57 | ```python 58 | condition = True 59 | print(2 if condition else 1/0) 60 | # Вывод: 2 61 | 62 | print((1/0, 2)[condition]) 63 | # Было вызвано исключение ZeroDivisionError 64 | ``` 65 | 66 | Во втором примере сначала собирается кортеж, а затем находится элемент под 67 | заданным индексом. Тернарный оператор на if-else следует обычной логике 68 | условного оператора `if`. Таким образом, если один из случаев может вернуть 69 | ошибку или обработка обоих случаев является слишком затратной операцией, то 70 | вариант с кортежами точно не стоит использовать. 71 | 72 | **Сокращенный тернарный оператор** 73 | 74 | В Python также существует более краткий вариант тернарного оператора. 75 | Этот синтакис был добавлен в Python 2.5 и может быть использован в новых 76 | версиях. 77 | 78 | **Пример:** 79 | 80 | ```python 81 | True or "Some" 82 | # Вывод: True 83 | 84 | False or "Some" 85 | # Вывод: 'Some' 86 | ``` 87 | 88 | Первое выражение (`True or "Some"`) возвращает `True` и второе - `Some`. 89 | 90 | Это удобно, когда нужно быстро проверить вернувшееся из функции значение и 91 | показать сообщение, если значения не было (вернулся `None`): 92 | 93 | ```python 94 | func_output = None 95 | msg = func_output or "Не было возвращено данных" 96 | print(msg) 97 | # Вывод: Не было возвращено данных 98 | ``` -------------------------------------------------------------------------------- /book/virtual_environment.md: -------------------------------------------------------------------------------- 1 | # Виртуальное окружение 2 | 3 | Доводилось слышать о `virtualenv`? Если вы начинающий, то, вероятно, нет. 4 | Для опытного же разработчика это неотъемлемая часть инструментария. 5 | 6 | Так что же такое `virtualenv`? `virtualenv` - это утилита для создания 7 | изолированных окружений в Python. Предположим - у нас есть приложение, которое 8 | требует версию 2 определенной библиотеки, однако, другое приложение требует 9 | версию 3. Как нам использовать и работать сразу с обоими? 10 | 11 | Если вы установите всё необходимое в `/usr/lib/python2.7/site-packages` 12 | (путь может отличаться в зависимости от платформы и версии языка), то легко 13 | можете случайно обновить уже установленный пакет. 14 | 15 | В другом случае, предположим, у нас есть полностью готовое приложение, и вы 16 | не хотите вносить изменения в его зависимости, в то же время вы работаете над 17 | другим приложением, которое требует последних версий библиотек из зависимостей 18 | первого приложения. 19 | 20 | Что вы будете делать? Использовать `virtualenv`! Оно создаст изолированные 21 | окружения для ваших приложений и позволит устанавливать пакеты именно в эти 22 | окружения, а не глобально. 23 | 24 | Для установки воспользуемся `pip`: 25 | 26 | ```bash 27 | $ pip install virtualenv 28 | ``` 29 | 30 | Две основные команды: 31 | 32 | - `$ virtualenv myproject` 33 | - `$ source myproject/bin/activate` 34 | 35 | Первая создаст новое изолированное окружение в папке `myproject`, а вторая 36 | активирует это окружение. 37 | 38 | При создании виртуального окружения вам нужно принять решение. Хотите ли вы, 39 | чтобы виртуальное окружение использовало пакеты, установленные глобально, но 40 | по умолчанию у `virtualenv` нет к ним доступа. 41 | 42 | Если вы хотите открыть `virtualenv` доступ к глобально установленным пакетам, 43 | то используйте флаг `--system-site-packages` во время создания окружения: 44 | 45 | ```bash 46 | $ virtualenv --system-site-packages mycoolproject 47 | ``` 48 | 49 | Вы можете деактивировать виртуальное окружение, набрав: 50 | 51 | ```bash 52 | $ deactivate 53 | ``` 54 | 55 | Команда `python` вновь будет использовать глобально установленный в системе 56 | интерпретатор Python после деактивации виртуального окружения. 57 | 58 | ## Бонус 59 | 60 | Вы можете использовать `smartcd` - библиотеку для bash и zsh, с помощью 61 | которой автоматически активировать и деактивировать виртуальное окружение 62 | при использовании команды `cd`. Больше информации здесь: 63 | [GitHub](https://github.com/cxreg/smartcd) 64 | 65 | Это было короткое введение в тему виртуальных окружений. Дополнительную 66 | информацию можно почерпнуть [здесь](http://docs.python-guide.org/en/latest/dev/virtualenvs/). 67 | -------------------------------------------------------------------------------- /cover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lancelote/interpy-ru/bec2931e23cbff9cbd02663fca3763b895e8d479/cover.jpg -------------------------------------------------------------------------------- /cover_small.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lancelote/interpy-ru/bec2931e23cbff9cbd02663fca3763b895e8d479/cover_small.jpg --------------------------------------------------------------------------------