Люди склонны думать, что Python динамически напечатан язык, потому что вы можете сделать это:
x = 10 x = "Hello"
Ну, вы можете выполнить аналогичное задание в ржавчине, которая является статически напечатанным языком.
let mut x = 10; let x = "Hello";
По общему признанию, я обманул здесь. Эта концепция называется тени, и ржавчина не позволит вам сделать это:
let mut x = 10; x = "Hello";
С другой стороны, затенение – это именно то, что делает Python при назначении имени объекту.
В Java или C# вы можете написать что -то вроде:
public class Example{ public static void main(String []args){ Object x = 10; System.out.println(x == 10); // error! x = "Hello"; // This is fine as we cast Object to String. System.out.println( ((String)x).equals("Hello")); } }
С внешней точки зрения, это напоминает Python, пока вы не поймете, что вам не нужно выполнять какое -либо кастинг.
Разница появляется, как только мы пытаемся предпринять конкретное действие:
x = 10 assert x == 10 x = "Hello" assert x == "Hello"
Эти линии в Python вполне хороши.
Не так в Java, как Java требует от нас типа X, что поддерживает конкретное сравнение, прежде чем мы его выполним. Таким образом, мы можем продолжать уверен, что все в порядке, если мы не столкнемся с страхом NullReferenceException во время выполнения.
public class Example{ public static void main(String []args){ String x = null; System.out.println(x.equals("Hello")); // NullReferenceException } }
По сути, Java должна знать, какой тип заранее, в то время как Python не очень заботится. Но поскольку Python сильно напечатан, вы можете полагаться на переводчика, чтобы потерпеть неудачу, пока Java счастливо компонент.
В Python
x = "Hello" y = None # x + y raises an exception.
Пока в Java:
public class Example{ public static void main(String []args){ String x = null; System.out.println( x + "hello"); } } // Prints "nullhello"
Такое поведение является реликвием печально известного Ошибка миллиардов долларов Анкет
Я называю это своей ошибкой в миллиард долларов. Это было изобретение нулевой ссылки в 1965 году. В то время я проектировал первую систему комплексной типа для ссылок на объектно -ориентированном языке (Algol W). Моя цель состояла в том, чтобы гарантировать, что все использование ссылок должно быть абсолютно безопасным, с проверкой, выполненной автоматически компилятором. Но я не смог устоять перед искушением поставить нулевую ссылку, просто потому, что его было так легко реализовать. Это привело к бесчисленным ошибкам, уязвимостям и сбоям системы, которые, вероятно, вызвали миллиард долларов боли и повреждения за последние сорок лет.
Современные реализации Java или C# предлагают способы избежать этого. Вот почему вы не должны пренебрегать дизайном языка. Люди, которые разработали первую версию C#, не исключая очевидных, а затем и известных недостатков в Java, теперь провозглашены для современного C#, что, по общему признанию, довольно хорошо, потому что, помимо прочего, они каким-то образом исправляли свои первоначальные ошибки. Чтобы быть справедливым, Java колебалась намного дольше, чтобы улучшить, несмотря на справедливую долю критики.
Например, ржавчина не страдает, поскольку это довольно недавний язык программирования. С другой стороны, в Котлине (еще один недавний язык), который имеет дело со старой кодовой базой Java, и совместимость – это вещь, вы также можете столкнуться с проблемой, несмотря на тот факт, что чистый котлин уничтожает проблему благодаря своей замыслу.
Давайте закончим с примерами из двух других языков:
JavaScript
"hello" + undefined // ends with "helloundefined"
PHP
$x = 10; $y = null; $x + $y; // Is this obviously 10? $0 == null; // This is 1, meaning true //What exactly is this expression? I have no idea //echo failed to give me any information. echo 0===null; // At least, php7 gave me a warning when I tried this "hello" + null;
Но вернемся к Python. По праву вы можете спросить, почему кастинг там не происходит.
Теоретически, вы можете охранять свой код следующим образом:
if isinstance(y, int): y + y
На практике чаще встречается:
try: y + y except TypeError: pass #or whatever you'd like to do
Философия Python утверждает, что легче просить прощения, чем просить разрешения.
Что это значит для вас?
Рассмотрим следующее:
from unittest import TestCase test = TestCase() class Animal: name = "Doggie" class Person: name = "Hubert" def get_name(x): return x.name test.assertEqual(get_name(Animal()), "Doggie") test.assertEqual(get_name(Person()), "Hubert")
Конечно, знаменитый уток на практике. На статически напечатанном языке вам нужно будет создать и реализовать интерфейс для достижения аналогичного результата. (См. Я не использовал здесь наследование . Если вам нужен хороший пример, я предлагаю изучить, как они реализовали композиция в котлине, например Здесь .. Ржавчина не хватает наследства полностью, реализуя [признаки] ( https://en.wikipedia.org/wiki/trait_(computer_programming) вместо.
Без сомнения, интерфейсы передают ваше намерение более безопасно и предсказуемо, но, честно говоря, беспокоятся о них каждый раз – это хлопот.
Теперь вам может быть любопытно, что происходит Когда get_name Получает тип, не содержащий имя поля. Давайте проверим это.
class Anything: pass with test.assertRaises(AttributeError) as _: get_name(Anything()) try: test.assertIsNotNone(Anything().name) except AttributeError: print("The name is not None for sure!")
The name is not None for sure!
По сути, мы получили исключение и подтвердили экспериментом, что поле имя не Нет Анкет Почему я это сделал? Чтобы продемонстрировать обеспечение безопасности Python по сравнению с другим основным динамическим языком на типике, JavaScript.
let anything = {}; let get_name = (x)=>x.name; get_name(anything.name) //undefined
Это важное различие в дизайне этих двух языков. Если вы пренебрегаете, чтобы осмотреть свой объект на поле имя , Python терпит неудачу, если этого не присутствует, в то время как JavaScript просто продолжает работать-какое поведение вместе со своим слабым характером и странными правилами принуждения может привести к абсолютно непредсказуемым результатам и труднодоступным ошибкам.
Теперь давайте вернемся к динамической природе Python. В объявлении класса Что угодно Мы опустили поле имя . Но мы можем сделать это.
car = Anything() car.name = "Škoda" test.assertEqual(get_name(car), "Škoda")
Фу, теперь это становится сложным. По умолчанию мы можем добавить поле во время выполнения, как мы хотим. Вы помните, как я упоминал интерфейсы? Как справится с статическим языком в реализации после богоподобного класса, который пытается справиться с разнообразием реального мира?
from typing import Optional class Car: def __init__(self, **kwargs): self.__dict__ = kwargs def is_truck(self)->bool: return hasattr(self, "maximum_load") def get_maximal_number_of_persons(self)->Optional[int]: try: return self.persons except AttributeError: return None def get_name_of_all_fields(self): return list(self.__dict__.keys()) def __str__(self): return f"Car: {self.name if hasattr(self, 'name') else 'Uknown'}" skoda = { "name": "Škoda", "persons": 5, "type": "Fabia", } tatra = { "name": "Tatra", "maximum_load": 1000, "fuel": "diesel", } skoda_car = Car(**skoda) tatra_truck = Car(**tatra) test.assertEqual(get_name(skoda_car), "Škoda") test.assertFalse(skoda_car.is_truck()) test.assertEqual(skoda_car.type, "Fabia") test.assertIsNotNone(skoda_car.persons) test.assertTrue(hasattr(tatra_truck, "fuel"))
Программисты в статически напечатанных языках кричат от боли. Какой ужас! Это абсолютно непредсказуемо! Это то, что слово динамический означает постоянное изменение.
На самом деле, в частности, не весь код в Python использует свою динамичность, поэтому вам не нужно так сильно беспокоиться. Чтобы принести больше порядка в динамическом хаосе, Python в третьей версии охватывает дополнительную статическую набор, которая становится лучше с каждой итерацией.
Но вернемся к нашему динамичному бизнесу. Рано или поздно каждый начинающий Pythonista сталкивается с полностью построением в методе или функции подписи, которая читается: (*args, ** kwargs)
Например:
def kill_them_all(*args, **kwargs): return f"I killed {args} and also {kwargs}" kill_them_all("dogs", "people", "bacteria", numbers=(1,2,3.4), function=print, any_object=object())
"I killed ('dogs', 'people', 'bacteria') and also {'numbers': (1, 2, 3.4), 'function':, 'any_object':
Эта легкая функция не знает по милости. Он принимает все, без каких -либо отношений с числами, типами или целью; В некотором смысле, вы только что потрясли руку окончательному хищнику Pythonic Jungle.
Почему доброжелательный диктатор создает такое ужасное существо, не бессмысленно глотать куски кода?
Почему действительно? Вы можете осмотреть args и Kwargs или даже лучше, вы можете подтолкнуть их к другому голодному горлу. Вот!
def print_only_cars(*args): if all(isinstance(obj, Car) for obj in args): print(*args) else: print("I refuse to print:", *args) print_only_cars(1,2,3) print(skoda_car, tatra_truck, Car())
I refuse to print: 1 2 3
Car: Škoda Car: Tatra Car: Uknown
Надеюсь, я продемонстрировал несколько аспектов динамического поведения Python. Какими бы ни были сторонники утверждения статического типирования, динамическое типирование не уступает, на самом деле, статическое типирование – это лишь подмножество упругого характера динамического набора.
Допустимо, что один платит за эту силу, а именно за меньшую читаемость, более медленную скорость исполнения и более сложный рефакторинг. С другой стороны, ограничить себя парадигмой статически, было бы глупо. Возьмите, к примеру, отличный современный язык, такой как ржавчина. Чтобы достичь аналогичных результатов, как показано здесь, если это даже возможно, это легко, потребуется погрузиться в тайный синтаксис макросов Rust, и там вся читаемость идет в ад.
Более того, будучи суперсетом статического набора, динамически напечатанных языков, а именно Python с аннотациями типа или TypeScript для JavaScript, получают выгоды от обоих миров.
Языковой дизайн имеет значение. Некоторые языки, даже если вы не согласны с тем, как они реализованы, придерживайтесь внутренней согласованности, в то время как другие – просто набор случайных правил; правила, которые их создатель нашел удобным в то время он/Она создавала язык и который, в конце концов, делает такие языки очень опасными местами для жизни и кодирования.
Там нет идеального языка. Все они ошибочны или неоптимальны, если вы предпочитаете. Тем не менее, некоторые более безопасны, чем другие. Если вы можете, выберите лучшие.
Оригинал: “https://dev.to/hanpari/dynamic-nature-of-python-and-language-design-4bhm”