Часті питання, python 3 для початківців і чайників
Деякі не зовсім очевидні речі, з якими стикаються початківці програмісти Python.
Чому я отримую виняток UnboundLocalError, хоча змінна має значення?
Може здатися несподіваним отримати UnboundLocalError в раніше чинному коді, в який додали операцію присвоювання десь всередині функції.
працює, але наступний код:
призводить до UnboundLocalError:
Це відбувається тому, що, коли ви робите присвоювання змінної в області видимості, вона стає локальної в цій області і приховує інші змінні з таким же ім'ям в зовнішніх областях.
Коли остання інструкція в foo привласнює нового значення змінної x. компілятор вирішує, що це локальна змінна. Отже, коли більш ранній print намагається надрукувати неініціалізованих змінну, виникає помилка.
В наведеному вище прикладі можна отримати доступ до змінної, оголосивши її глобальної:
Ви можете зробити подібну річ у вкладеній області видимості використанням ключового слова nonlocal:
Які правила для глобальних і локальних змінних в Python?
В Python, змінні, на які тільки посилаються всередині функції, вважаються глобальними. Якщо змінної присвоюється нове значення де-небудь в тілі функції, вважається, що вона локальна, і, якщо вам потрібно, то потрібно явно вказувати її глобальною.
Хоча це трохи дивно на перший погляд, це легко пояснити. З одного боку, вимога global для привласнюються змінних запобігає ненавмисні побічні ефекти в bar. З іншого боку, якщо global був обов'язковим для всіх глобальних посилань, ви б використовували global весь час. Ви повинні були б оголосити як глобальну кожну посилання на вбудовану функцію або компонент імпортованого модуля.
Чому анонімні функції (lambda), певні в циклі з різними значеннями, повертають один і той же результат?
Наприклад, ви написали наступний код:
Це дає вам список з 5 функцій, які вважають x ** 2. Можна очікувати, що, будучи викликаними, вони повернуть, відповідно, 0, 1, 4, 9, і 16. Однак, ви побачите, що всі вони повертають 16:
Це трапляється, оскільки x не є локальною для lambda. а визначена у зовнішній області видимості, і виходить тоді, коли вона викликається - а не коли визначається.
В кінці циклу, x = 4. тому всі функції повертають 4 ** 2. тобто 16. Це можна також перевірити, змінивши значення x і подивившись на результат:
Щоб уникнути подібного, необхідно зберігати значення змінних локально:
Тут, n = x створює локальну для функції змінну n і обчислюється в момент визначення функції:
Це стосується не тільки до анонімних, а також і до звичайних функцій.
Як організувати спільний доступ до глобальних змінних для декількох модулів?
Канонічний спосіб організувати подібний доступ - це створити окремий модуль (часто званий config або cfg). Просто додайте import config в кожен модуль програми. При цьому модуль стає доступний через глобальне ім'я. Оскільки існує тільки один екземпляр модуля, будь-які зміни, зроблені в модулі відображаються всюди. наприклад:
З тих же міркувань, модулі можна використовувати як основу для імплементації Сінглтона.
Як правильніше використовувати імпортування?
У загальних випадках не використовуйте from modulename import *. Це засмічує простір імен того, хто імпортує. Деякі люди уникають цієї ідіоми навіть для тих небагатьох модулів, які були спроектовані, щоб так імпортуватися. Це такі модулі як Tkinter і threading.
Імпорт модулі на початку файлу. Це відповідає на питання, які модулі вимагає Ваш код і знаходиться ім'я модуля в області видимості. Запис по одному імпорту на рядок спрощує додавання і видалення операторів імпорту, але множинний імпорт буде займати менше місця на екрані.
Хороша практика, якщо Ви імпортуєте модулі в наступному порядку:
- стандартні бібліотечні модулі (наприклад, sys, os, getopt, re)
- модулі сторонніх розробників (все, що встановлено в директорії site-packages) - наприклад, PIL, NumPy і т.д.
- локально створені модулі
Іноді буває необхідно помістити імпорт в функцію або клас, щоб уникнути проблем з циклічним імпортом. Gordon McMillan радить:
Циклічний імпорт відмінно працює, якщо обидва модуля використовують форму import
В цьому випадку, якщо другий модуль використовується тільки в одній функції, то імпорт можна легко помістити в цю функцію. На той час, як він буде викликаний, перший модуль вже закінчить ініціалізацію і другий модуль здійснить свій імпорт.
Може виявитися необхідним перемістити імпорт з початку файлу, якщо один з модулів платформно-залежний. У цьому випадку імпорт всіх модулів на початку файлу виявиться неможливим. У цій ситуації хорошим рішенням буде імпорт потрібних модулів у відповідному платформно-залежному коді.
Переносите імпорт у вкладені області видимості, такі як визначення функцій, тільки якщо Ви зіткнулися з проблемою, наприклад циклічного імпорту, або якщо Ви намагаєтеся скоротити час ініціалізації модуля.
Ця техніка корисна, якщо багато хто з імпорту не є необхідними, і залежать від того, як програма буде виконуватися. Ви також можете помістити імпорт в функцію, якщо конкретні модулі використовуються тільки в цій функції. Зверніть увагу, що завантажити модуль в перший раз може виявитися дорого через затримку на ініціалізацію модуля, проте повторні завантаження "безкоштовні", вони стоять тільки пари пошуків в словниках. Навіть якщо ім'я модуля зникло з області видимості, модуль швидше за все до сих пір знаходиться в sys.modules.
Чому значення за замовчуванням розділяються між об'єктами?
Цей тип помилки часто зустрічається серед початківців. Припустимо, функція:
У перший раз, коли ви викликаєте функцію, mydict містить одне значення. Другий раз, mydict містить 2 елемента, оскільки, коли foo () починає виконуватися, mydict вже містить елемент.
Часто очікується, що виклик функції створює нові об'єкти для значень за замовчуванням. Але це не так. Значення за замовчуванням створюються лише одного разу, коли функція визначається. Якщо цей об'єкт змінюється, як словник в нашому прикладі, наступні виклики функції будуть використовувати змінений об'єкт.
За визначенням, незмінні об'єкти (числа, рядки, кортежі і None), безпечні при зміні. Зміна змінюваних об'єктів, таких як словники, списки, і екземпляри призначених для користувача класів може привести до несподіваних наслідків.
Тому гарним вибором буде не використовувати змінні об'єкти в якості значень за замовчуванням. Замість цього, використовуйте None і всередині функції, перевіряйте аргумент на None і створюйте новий список / словник. Наприклад, не пишіть:
Однак, ця особливість може бути корисна. Коли у вас є функція, яка довго виконується, часто застосовується техніка - кешування параметрів і результату кожного виклику функції:
Як передати опціональні або іменовані параметри з однієї функції в іншу?
Отримати такі параметри можна за допомогою специфікаторів * і ** в списку аргументів функції; вони повертають кортеж позиційних аргументів і словник ім'ям параметрів. Після цього Ви можете передати їх в іншу функцію, використовуючи в її виклику * і **:
Чому зміна списку 'y' змінює також список 'x'?
Якщо ви написали код:
ви, можливо, будете здивовані тому, що додавання в y змінює також і x.
Два факти призводять до такого результату:
- Змінні - це просто посилання на об'єкти. y = x НЕ створює копію списку - це просто створює змінну y. яка посилається на той же об'єкт, що і x.
- Списки змінюються.
Після виклику append. вміст об'єкта було змінено з [] на [10]. Оскільки x і y посилаються на один і той же об'єкт, використання будь-якого з них дає нам [10].
Якщо ми використовуємо незмінні об'єкти:
ми можемо бачити, що x і y більше не рівні, оскільки числа незмінні, і x = x + 1 не змінює число 5 шляхом збільшення. Замість цього, створюється новий об'єкт 6 і присвоюється змінної x (тобто, змінюється то, на який об'єкт посилається x). Після цього у нас 2 об'єкти (6 і 5) і 2 змінні, які на них посилаються.
Деякі операції (наприклад y.append (10) і y.sort ()) змінюють об'єкт, в той час, як зовні схожі операції (наприклад y = y + [10] і sorted (y)) створюють новий об'єкт. Взагалі в Python (і у всіх випадках в стандартній бібліотеці), метод, який змінює об'єкт, повертає None. щоб допомогти уникнути помилок. Тому, якщо ви написали
думаючи, що це дасть вам відсортовану копію y, ви замість цього отримаєте None. що швидше за все призведе до легко діагностується помилку.
Однак, існує один клас операцій, де одна і та ж операція поводиться по-різному з різними типами: розширені оператори присвоювання. Наприклад, + = змінює списки, але не кортежі або числа (a_list + = [1, 2, 3] еквівалентно a_list.extend ([1, 2, 3])) і змінює список, в той час, як some_tuple + = ( 1, 2, 3) і some_int + = 1 створюють новий об'єкт.
Якщо ви хочете знати, чи посилаються 2 змінні на один об'єкт чи ні, ви можете використовувати оператор is. або вбудовану функцію id.
Як створювати функції вищого порядку?
Є два шляхи: використовувати вкладені функції або викликаються об'єкти. Наприклад, з використанням вкладених функцій:
Використання викликається об'єкта:
В обох випадках,
дає функцію, що (наприклад) taxes (10e6) == 0.3 * 10e6 + 2.
Використання викликається об'єкта - трохи повільніше, і в результаті виходить більше коду. Однак, зауважте, що кілька функцій можуть розділяти свою сигнатуру за допомогою наслідування:
Об'єкт може зберігати свій стан для кількох викликів:
Тут inc. dec. reset виступають в ролі функцій, які поділяють одну і ту ж змінну.
Як скопіювати об'єкт в Python?
Послідовності можуть бути скопійовані шляхом зрізів:
Як дізнатися доступні методи і атрибути об'єкта?
dir (x) повертає список методів і атрибутів.
Як можна дізнатися ім'я об'єкта?
Взагалі кажучи, ніяк, оскільки об'єкти насправді не мають імен. Важливо: присвоювання завжди пов'язує ім'я з об'єктом. Це вірно і для інструкцій def і class.
Можливо, клас має ім'я: однак, хоча він пов'язаний з двома іменами і запитується через ім'я B. створений екземпляр все ще вважається екземпляром класу A. Однак, неможливо сказати, ім'я екземпляра a або b. оскільки обидва вони пов'язані з одним і тим же значенням.
Який пріоритет у оператора "кома"?
Кома не є оператором в Python.
Оскільки кома - НЕ оператор, але роздільник між виразами, приклад вище виповнюється як якби було введено:
Те ж саме вірно і для операторів присвоювання (=. + = І інші). Вони не є операторами як такими, а лише синтаксичними роздільниками в операціях присвоювання.
Чи є в Python еквівалент тернарного оператора "?:" В C?
Чи можна писати обфусцірованние однострочнікі?
Не намагайтеся це робити вдома!