Ввод в пустую переменную из input
Это всё — вторая команда в вашем вопросе ( a = «» ) бы только уничтожила только что принятую информацию ‘Володя’ .
Да, вы теперь можете хотеть проверить, какое значение в переменной a , и так можете после команды input(‘Задайте ваше имя: ‘) писать как следующую команду
print(a)
и получите соответственный вывод, в нашем примере
Володя
Отслеживать
ответ дан 30 июл 2018 в 19:03
16k 4 4 золотых знака 19 19 серебряных знаков 30 30 бронзовых знаков
from colorama import init, Fore, Back, Style import win32console win32console.PyConsoleScreenBufferType(win32console.GetStdHandle(\ win32console.STD_OUTPUT_HANDLE)) # p = _ p.SetConsoleFont(8) init(convert=True) class GuessNumber: winner = False number = int def __init__(self): print(Style.BRIGHT + Fore.GREEN +"Угадай число от 0 до 20, у тебя три попытки") print("-------------------------------------------") self.game() def rnd(self): self.random_number = randrange(0, 20, 1) def check(self, num): self.number = num if self.number == self.random_number: self.winner = True else: return def game(self): count = 0 self.rnd() while True: count += 1 print(count, "попытка") user_number = int(input("Введите число:")) print("----------------------") self.check(user_number) if count == 3: print("К сожалению ты проиграл :(") print("Компьютер загадал число", self.random_number) input("Для выхода нажмите ENTER") break if self.number > self.random_number: print("Загаданое число меньше") elif self.number < self.random_number: print("Загаданое число больше") if self.winner: print("Ура ты победил. ") print("Компьютер загадал число", self.random_number) input("Для выхода нажмите ENTER") break def main(): app = GuessNumber() if __name__ == "__main__": main()
Отслеживать
ответ дан 4 авг 2018 в 11:34
3,005 2 2 золотых знака 14 14 серебряных знаков 22 22 бронзовых знака
Обратный слеш для разрыва строки внутри скобок не нужен. Т.е. без обратного слеша будет работать.
4 авг 2018 в 14:14
Это для сайта при вставке кода)
4 авг 2018 в 15:10
a = [] while True: c = input('Как вас завут: ') b = input('\nПродолжить да & нет: ') a.append(c) if b == 'нет': break if b == 'да': print(c) else: print(c, b)
Отслеживать
ответ дан 31 июл 2018 в 7:30
11 4 4 бронзовых знака
Хотелось бы отметить, что в питоне нет переменных, как таковых. Когда вы пишете код вроде:
>>> a = 5
Вы создаете ссылку "a" на объект 5. Также, в питоне нет "пустых" переменных.
>>> a = "" # Это создаст объект пустой строки. >>> type(a)
>>> x = 5 # Инициализация объекта 5 >>> y = "" # Инициализация объекта пустой строки >>> y = x # Создание дополнительной ссылки на объект 5 >>> y 5
Если вы хотите создать копию объекта, а не ссылку на него, воспользуйтесь модулем copy. За дополнительной информацией о модуле copy посмотрите: https://docs.python.org/3.7/library/copy.html
Метод input('Необязательное праглашение ввода:') автоматически создает объект типа str. Но его можно преобразовать в int: int(input('Необязательное праглашение ввода:')) . И так как это обычный объект первого класса, его можно передавать в качестве аргумента без создание ссылки на него:
>>> for i in range(int(input('enter number of iterations: '))): . print(i) . enter number of iterations: 5 0 1 2 3 4 >>>
Ссылку на объект нужно создавать в том случае, если этот объект используется в коде много раз. Ну или по ситуации.
Неинициализированное состояние переменной
Из серии "за что мне нравится Python". Сегодня сформулировал у себя в голове еще одну приятную штуку.
Часто бывает нужно, чтобы у переменной какого-то типа было "никакое" или "неопределенное" значение. Скажем, еще непосчитанное количество, заданное int'ом или время создания объекта, который еще до конца не создан, заданное timestamp'ом.
В языке со статическим типизированием у элементарных типов (целое, дата/время) таких специальных значений нет, и чтобы это обойти есть, в общем-то, всего два подхода:
- Если вы используете не все значения типа, то можно зарезервировать какое-то из значений для показывания неопределенности. Например значение "-1" для количества, которое должно быть неотрицательным или "31.12.1899" для даты. Минусов у этого решения я усматриваю два. Во-первых, ухудшается читаемость кода: везде, где вы встречаете " if(x!=-1) " мозгу приходится разрешать лишнюю абстракцию, вспоминая, что "-1" в данном случае означает неопределенность". Хотя это можно обойти заданием константы с понятным именем. Второй минус более синий. Поскольку компилятор не в курсе, что это значение особенное, он будет продолжать с ним работать без всяких ошибок. Скажем, сложит известное количество с неизвестным. * Второй способ, более безопасный — сделать отдельный маленький класс, который будет хранить значение нужного типа, а также признак инициализированности. И при всех попытках прочитать значение сначала проверять этот признак и если надо, выкидывать exception. Недостаток этого способа заключается в простом неудобстве. Придется наверняка делать у этого класса некий метод доступа "value()", в котором будут идти проверки, и вместо " x=y+z " писать что-то вроде " x.value=y.value+z.value ". Кроме того, в зависимости от языка, еще придется заботиться об управлении памятью, о том, что классы передаются по умолчанию по ссылке, а не по значению. Ну и кроме того, сам класс, не несущий никакой творческой нагрузки, просто загромождает код.
А что же нам предлагает Python. Поскольку у переменной нет привязки к типу, то любой переменной в любой момент можно назначить значение None (аналог NULL'а, Nil'а). А потом, когда надо, присвоить ей нужное реальное значение. В итоге:
- Значение None четко означает отсутствие значения.
- Проверки красиво читаются:
if not create_date: create_date=datetime.today()
Вот так все логично и лаконично.
Комментарии: 14
Не буду разводить здесь Вашу нелюбимую дискуссию, но мне за тот же null (и не только) нравится PHP. 🙂
Иван Сагалаев
Оне не моя нелюбимая :-). Я просто не знаю PHP настолько близко, чтобы о нем внятно много говорить. Однако, что я таки про него знаю — это то, что там типизация не только динамическая, но и свободная (loose). Свободная в том смысле, что типы в выражении всегда по возможности приводятся к чему-то общему. И в PHP можно сделать так:
$i=5; $n=null; echo $i + $n; //выдаст 5
То есть можно свободно использовать неинициализированную переменную в выражениях. Питон принципиально отличается тем, что типизация динамическая, но строгая (strict). Если переменной дано значение какого-то типа, то как раз у значения этот тип реально есть, и он не будет автоматически приводиться к любому другому типу. Например аналогичный примеру код на Питоне ругнется exception'ом. Надо еще сказать, что в Питоне по этому поводу нет никакой догмы вроде "никогда ничего не приводить". Тут скорее руководствуются чисто практическим аспектом: делать то, что логично в данном случае. Так например кое что с None все-таки можно делать. Например, сравнивать с целыми и строками (но не с датой). И для своих собственных типов пользователь тоже может сам определить результаты операция с None.
if not s это крайне неудачная конструкция - слишком много в мире вещей, которые приводятся к False. Например, нельзя отличить s="" и s=None Так что никуда не денешься, придётся писать if s==None Кстати, и в питоне, и в php есть другое решение - совсем не объявлять неинициализированную переменную. Попросту del её. if "a" in locals(): // делать что-то с a else: // делать что-то без а Или даже
try: // делать что-то с a except NameError: // делать что-то без а
Иван Сагалаев
Согласен, " if not s " может быть неудачной (хотя и не всегда). В любом случае, явная проверка на None (" if s==None " или даже красивей " if s is None ") все же гораздо лучше, чем проверка на что-то другое. Но вот решения с "in locals()" или exception'ом я бы не приветствовал :-). Во-первых просто громоздко, а во-вторых они, опять-таки, уводят от прямого сообщений "переменной нет" к некой ширме "это означает, что переменной нет".
В PHP действительно нет строгой типизации. Имея какой-никакой опыт в программирповании думаю, что это чаще плохо, чем хорошо. Поскольку оболтусы называющие себя Программистами, даже не догадываясь что существуют некие понятия о хорошем и плохом стиле программирования, выдают такой грязный код, который работает только до первого внесения изменений в него. И отсутствие строгой типизиции в PHP усугубляет ситуацию. По поводу же значений Null и их поведения в операциях в PHP - все сказано в документации. Нужно иногда ее читать. Это особенность, о которой все известно, ну или почти все :). А кому неизвестно - это его проблемы. Тем более, что приведенный пример ярко демонстрирует возможности языка и небезопасный стиль программирования. Если полагаться только на типизацию, то человеческий фактор все равно может сыграть свою негативную роль.
P.S. Если переменной присвоено значение null - она, строго говоря, инициализирована. Ее можно проверить is_null(). И существование переменной тоже можно проверить. Это работа программиста.
уводят от прямого сообщений “переменной нет” к некой ширме “это означает, что переменной нет”.
Наоборот 🙂 Присваивание None/null это не более, чем конвенция, может быть, более удобная, чем присваивание -1. Если переменной нет - пусть её не будет и в namespace. Ну да, in locals() это немного уродливо, но ведь никто не мешает сделать функцию. В php из-за его свободной типизации этот вариант даже предпочтительней: если $n не объявлено, echo $n +1; просто вылетит, проверка isset() абсолютно прозрачна.
Иван Сагалаев
Конечно конвенция. Но это стандартизованная конвенция, и потому всем очевидная. Поэтому-то мне и кажется, что менее читаемые варианты все же ничем не выигрывают. P.S. Как приятно — такая маленькая тема, а так подробно в комментариях раскрывается :-). Большое всем спасибо!
По моему, инициализировать переменные нужно всегда. И инициализировать их так, что б не было больно при возможных проблемах. Будущий массив инитить пустым массивом, строку - пустой строкой, int - нулем, коэффициент - 1, и так далее.
Тогда не будет проблем и в php с его приведением типов, и в python.
$i=5;
$n=null;
echo $i + $n; //выдаст 5
А как, тогда, насчёт такого примера (как уже упомянул CR): $i=5;
unset ($n):
echo $i +$n; // выдаст 5 и ругнется на неинициализированную переменную Проверять $n на никакое значение можно так: if (isset($n)) <. >else <. >// isset() не ругается а молча делает своё дело
Лучше так:
echo (isset($n))?$i+$n:"blabla";
Ну что же, давайте пофлеймим. а) чего -то я не пойму а если в Java делается return ; то что же возвращается? Не иначе как тот самый Null только в "обертке". Так что причем тут Питон не пойму. б) - ну и про наше любимое В Руби вообще нельзя использовать переменную до ее определения (если только это не свойство обьекта - тогда оно будет nil)
class Dummy def value @val end end Dummy.new.value # вернет nil
а поскольку в контексте функции доступны методы и переменные то первая же попытка использовать "ни то ни другое" вызовет ошибку подобную питоновской. А дальше вот начинается типа правильная штука. Любой обьект отвечает на простейшее сообщение nil?
nil.nil? # true 3.nil? # false
причем это совершенно не то же самое что empty($var). Потому что для этого есть "empty?" (но на него не отвечает nil)! Более того, когда например массив очищается от nil'ов методом compact! ваш собственный обьект тоже спросят "а не nil? ли ты?" Дабы получить традиционное PHP-поведение в рельсах добавлен метод blank?
nil.blank? # true ''.blank? # true и тд ' '.blank? # true, ага? 'foo'.blank? # false
По поводу "красиво читаются" это даааа. (я очень полюбил unless, until и пр.)
@creation_date = Date.today unless @creation_date
Поскольку Рубин довольно строгий в булях ВСЕ кроме false и nil приводится к true. А вот в PHP с этим забавно (и неприятно) потому что к false приводятся и пустой массив, и пустая строка и integer-ноль. что messy и подкладывает засаду
Max Ischenko
Согласен, только я всегда использую "if foo is [not] None", даже когда в этом нет прямой необходимости (как в случае с датами). Это ИМХО делает код яснее.
А вот в PHP с этим забавно (и неприятно) потому что к false приводятся и пустой массив, и пустая строка и integer-ноль… что messy и подкладывает засаду
Кому как. Мне лично - "приятно". Потому что часто это позволяет сэкономить код. А чтобы не было засад, нужно пользоваться операторами === и !==
7. Типы данных в Python
В предыдущем уроке на вас обрушился, возможно, новый поток информации. Начнем с разбора понятия «тип данных» в программировании и необязательно на Python. Тип данных – это множество значений и операций, которые можно совершить над ними. Дальше углубляться не будем. Какие стандартные типы данных есть в Python?
В Python есть числа, строки, списки, словари, кортежи, множества и логический тип данных. Списки, словари, кортежи и множества мы рассмотрим позднее, по каждому из них будет несколько уроков. Итак, типы данных в Python можно классифицировать следующим образом:
- изменяемые (множества, списки, словари);
- неизменяемые (кортежи, строки, числа);
- упорядоченные (списки, строки, кортежи, словари);
- неупорядоченные (множества).
Важной особенностью языка Python является его типизация. Python относится к языкам с неявной сильной динамической типизацией. Что это означает? Неявная типизация: вам не нужно при объявлении переменной указывать конкретный тип данных, к которой она относится, как, например, в языке С++:
int a = 10
В Python объявить переменную очень просто:
a = 10
Под динамической типизацией понимается то, что поиск ошибок будет осуществляться на стадии выполнения программы. В языках со статической типизацией поиск ошибок выполняется на стадии компиляции. Например, в Python возможно присвоить одной переменной сначала объект одного типа данных, а потом другого:
a = "Hello"
a = 1
Под сильной (или строгой) типизацией понимается то, что язык Python не позволяет смешивать типы данных. Если вы задали переменную как число, вы не сможете сложить ее со строкой:
a = 10
print('Десять = ' + a)
Это увеличивает надежность кода, так как вам необходимо явно преобразовать число к строке:
a = 10
print('Десять = ' + str(a))
Модель данных
Прежде чем переходить к конкретным типам данных, поверхностно рассмотрим, что такое модель данных, как создаются объекты в памяти и работу операции присваивания ( = ).
Чтобы объявить переменную в языке Python, необходимо указать ее имя, поставить знак присваивания ( = ) и написать значение, которое будет сохранено в переменной. Пример:
a = 10
Переменной с именем a мы присвоили число десять. Целочисленное число десять является объектом, как и все в Python: числа, строки, списки и т.д. Объект является абстракцией данных. Данные – это не только объекты, но и отношения между ними. Объект состоит из трех частей: тип, идентификатор и значение.
Что происходит при инициализации переменной на уровне интерпретатора? Создается целочисленный объект 10 , который сохраняется где-то в памяти. Данный объект имеет идентификатор, значение 10 и целочисленный тип. С помощью оператора присваивания ( = ) создается ссылка между переменной a и объектом 10 , целочисленного типа.
Имя переменной не должно совпадать с ключевыми словами Python. Чтобы это проверить, можно воспользоваться методом iskeyword() из модуля keyword .
import keyword
keyword.iskeyword("for") # => True
Рассмотрим следующий пример, чтобы лучше понять, как работает присвоение в Python:
a = 5
b = 10
print(id(a)) # => 140732499849616
print(id(b)) # => 140732499849776
a = b
print(id(a)) # => 140732499849776
Мы используем функцию id , чтобы определить идентификатор, на который ссылается переменная. Не привязывайтесь к конкретным числам, у вас они будут другими. Обратите внимание, что после присвоения поменялся идентификатор, на который ссылается переменная. Мы об этом еще вспомним, когда будем говорить про копирование списков. А еще вспомним, когда будем знакомиться с аргументами и параметрами функций в Python.
Чтобы узнать тип переменной, воспользуйтесь функцией type() :
a = 5
print(type(a)) # =>
Теперь вы готовы к разбору конкретных типов данных в Python.
Числа
С числами в Python можно выполнять различные математические операции:
a = 5 + 6
b = (4 * 6) + 12
c = 2 ** 16 # возведение в степень
d = 3 / 2 # => 1.5
При помощи функции round() можно округлять результат до определенного знака:
d = 10 / 6
print(round(d, 5)) # => 1.66667
Остаток от деления можно найти при помощи оператора % :
d = 10 % 6
d # => 4
С операторами сравнения вы уже познакомились из практики предыдущего урока.
print(10 > 5) # => True
и так далее…
Так как язык строго типизированный, часто необходимо преобразовать строку в число. Это можно сделать при помощи функции int() :
a = '100'
b = int(a)
print(type(a)) # =>
print(type(b)) # =>
Чтобы получить двоичное или шестнадцатеричное значение числа, используются функции bin() и hex() , соответственно. Кстати, результат является строкой:
a = 16
print(bin(a)) # => 0b10000
print(hex(a)) # => 0x10
Так же вы можете комбинировать операции и функции как угодно:
a = bin(int('100') + 20)
print(a) # => 0b1111000
Для более сложных математических операций существует модуль math , о котором поговорим позже. Например, найдем корень из числа десять:
import math
print(math.sqrt(10))
Подведем итог. С числами могут производиться различные математические операции и преобразования. В Python есть целые, числа с плавающей точкой и комплексные.
Строки
Строками в Python называется последовательность символов, обрамленных кавычками. Строки являются неизменяемым типом данных. Строки могут обрамляться как одинарными, так и двойными кавычками. Так же можно присвоить переменной несколько строк, для чего текст ограничивается тремя подряд кавычками:
a = 'Hello'
a = "Hello"
a = """
Несколько
Строк
"""
Строки можно складывать (конкатенировать) в одну. О конкатенации мы поговорим позже в теме про списки.
a = 'Hello '
b = "World"
print(a + b) # => Hello World
Строки можно умножать на число. Число указывает на количество раз, сколько строка будет повторена.
Строки в Python являются упорядоченным типом данных, как и списки, поэтому к отдельным символам можно обращаться по индексу. Индексация начинается с нуля:
a = 'Hello'
print(a[0]) # => H
Так же можно обратиться к последнему символу, если использовать отрицательное значение. Про обратное индексирование еще вспомним в теме про списки.
a = 'Hello'
print(a[-1]) # => o
В Python возможно сделать срез строки (про срезы будем говорить подробнее позднее):
a = 'Hello'
print(a[0:3]) # => Hel
print(a[1:]) # => ello
print(a[-3:]) # => llo
print(a[::2]) # => Hlo
Чтобы посчитать количество символов в строке (или количество элементов в списке), используйте функцию len() :
a = 'Hello'
print(len(a)) # => 5
Уже не раз звучали понятия «функция» и «метод». В чем разница? Как правило, метод привязан к объекту конкретного типа, а функция является более универсальной и может быть применена к объектам разного типа.
Про форматирование строк вы узнаете в следующем уроке.
Булевы значения
Это самый примитивный тип данных в Python, да и в любом языке программирования, который может принимать два значения: истина ( True ) или лож ( False ). Небольшая оговорка, в Python ложными и истинными значениями считаются не только True и False .
К истинным значениям в Python относятся:
- любое число не равно нулю;
- любая непустая строка;
- любой непустой объект.
К ложным значениям в Python относятся:
- ноль ( 0 );
- None ;
- пустая строка;
- пустой объект.
Чтобы проверить, является ли значение объекта ложным или истинным, используйте функцию bool() :
list_a = [1, 2, 3]
list_b = []
num_a = 0
str_a = ''
print(bool(list_a)) # => True
print(bool(list_b)) # => False
print(bool(num_a)) # => False
print(bool(str_a)) # => False
Еще ближе познакомитесь с логическим типом данных в уроке, посвященном условной конструкции if/else .
Неопределенный тип данных None
Неопределенное значение None можно отнести к отдельному типу данных. Если присвоить значение None переменной, то она будет сброшена в исходное состояние.
Списки, словаря, кортежи и множества
Это очень обширная и важная тема, о которой мы поговорим позднее и подробнее, начиная с этого урока.
Список – это последовательность разделенных запятой элементов и обрамленная квадратными скобками, например:
a = [1, 2, 3, 4, 5]
b = ["Hello", 1, 2, a]
Кстати, такая форма создания списков (и не только) называется литеральная. Литерал в программировании – это выражение, которое создает объект.
Словарь – это упорядоченный тип данных, в котором значения хранятся в виде ключ/значение. В других языках программирования он может называться ассоциативным массивом. Выглядит он так:
c = 1: "Один",
2: "Два"
>
Обратите внимание, словарь в Python задается в фигурных скобках.
Кортеж – это тот же список, но только неизменяемый. Он выглядит так:
d = (1, 2, 3)
То есть элементы в кортеже помещаются в круглые скобки.
Множество – неупорядоченный список уникальных элементов. Например:
e =
e # =>
Элементы множества записываются в фигурных скобках через запятую.
В этом уроке мы разобрали, что такое модель данных, какие основные типы данных есть в Python, а так же начали знакомиться со списками, словарями, кортежами и множествами.
Переменные и базовые классы данных в Kotlin
Переменные в Kotlin объявляются с помощью ключевых слов var или val, за которыми идет имя переменной, через двоеточие указывается ее тип – класс данных.
Переменной, объявленной через val , значение в программе можно присвоить только один раз. В дальнейшем значение можно только читать, val-переменные являются неизменяемыми.
fun main() { var a: Int a = 5 println(a) }
Приведенный выше код правильный, но IntelliJ будет подсказывать, что его можно улучшить. Поскольку в программе мы не меняем значение переменной, ее следовало бы объявить через val .

Однако поступим иначе. В процессе выполнения программы будем менять значение переменной:
fun main() { var a: Int a = 5 println(a) a = a + 10 println(a) }
Тут тоже не все гладко. IntelliJ советует совместить объявление переменной с присвоением ей значения.

Действительно, это уместно в данном случае. Ведь мы, например, не запрашиваем значение с ввода после выполнения каких-либо предварительных действий.
fun main() { var a: Int = 5 println(a) a = a + 10 println(a) }
Теперь IntelliJ советует избавиться от Int , так как по значению и так понятен тип переменной.

То есть Kotlin способен вывести тип переменной из присвоенных ей данных. Это называется автоматическим определением типов. Поэтому приводим код к такому виду:
fun main() { var a = 5 println(a) a = a + 10 println(a) }
Это не значит, что переменная стала без типа. Kotlin – статически типизированный язык. Просто тип переменной был выведен, исходя из ее значения. Чтобы увидеть тип переменной в IntelliJ, надо установить на нее курсор и нажать Ctrl + Shift + P .
Осталось последнее замечание. IntelliJ считает, что добавление значения к переменной лучше записывать в сокращенной форме:
fun main() { var a = 5 println(a) a += 10 println(a) }
В качестве вещественного типа по умолчанию используется класс Double :
fun main() val a = 3.4 println(a) >

В данном случае тип переменной автоматически определится как Double. В Kotlin есть и другие числовые типы помимо Double и Int – это Float , Long , Short , Byte .
fun main() { val a = true val b = false var c: Boolean = a > b println(c) // true c = a b println(c) // false }
В Kotlin есть как строковый, так и символьный типы:
fun main() { val s = "Hello" val s2: String val c = 'W' val c2: Char = s[0] s2 = s + c println(s2) // HelloW println(c2) // H }
В Kotlin есть классы коллекций – списки (List), словари (Map), множества (Set).
Константы
Переменные, объявленные через val , – не константы. Ведь им не обязательно сразу присваивать значение. Это происходит в процессе выполнения программы. Например:
fun main() { val a: String = readln() println(a) }
Здесь более очевидно, что до компиляции значение переменной a неизвестно. Присвоение значения происходит в процессе выполнения программы, и значением может быть любая строка.
С другой стороны, в Kotlin есть, так сказать, настоящие константы – константы времени компиляции. Их значение известно заранее, "вшивается" в программу в момент ее компиляции.
Объявление и определение констант происходит за пределами функции. Потому что код внутри функций выполняется при исполнении программы, а сигнатуры функций и полей должны быть известны на момент компиляции.
const val N: Int = 10 fun main() println(N) >
Модификатор const не получится использовать внутри функции. Это не значит, что все переменные за пределами функции должны быть константами. Там могут быть и обычные переменные. Очевидно, сочетания const var быть не может.
Константы могут принадлежать только числовым типам, строковому, символьному, а также Boolean .
Nullable-типы
В Kotlin обычным типам нельзя присвоить литерал null – значение, говорящее, что переменная не связана с обычным объектом. Подобное ограничение в Kotlin призвано обеспечить nullable-безопасность, то есть таким образом избегается ряд ошибок при выполнении программы. Например, определенная функция не предполагает, что ей передадут null , и не умеет обрабатывать это значение, но получает null . В этом случае в процессе выполнения программы будет выброшено исключение.
Во избежании подобного, обработка потенциально возможного null перекладывается на плечи программиста в принудительном порядке. Либо он напишет функцию так, чтобы она могла принимать и обрабатывать null , либо проверит значение на неравенство null до его передачи в качестве аргумента в функцию.
С другой стороны, в Kotlin у каждого типа без поддержки null имеется аналогичный ему с поддержкой null-значения. При объявлении типа, второй отличается от первого знаком вопроса в конце. Например, есть два строковых типа: String – без поддержки null и String? с поддержкой null .

На скрине показано, что попытка присвоить null переменным a и c приводит к ошибке. В то же время, для nullable-переменных это позволительно, хотя не обязательно. Их значениями также могут быть числа и строки, как у обычных переменных.
При этом в функцию, параметр которой не поддерживает null , передать nullable-аргумент нельзя. Компилятор Котлина не даст скомпилировать такую программу.

Встроенные функции readln() и readlnOrNull() возвращают строки, прочитанные с ввода. Функция readlnOrNull() потенциально может вернуть не только строку, но и значение null . Поэтому возвращаемый из нее тип String? . Переменную типа String? мы не можем передать в нашу функцию exPrint() , даже если значение этой переменной не будет null . Котлин – строго типизированный язык, поэтому String? и String для него – разные типы.
Для обработки nullable-переменных в Kotlin предусмотрено несколько особых операторов, которые будут рассмотрены в следующем уроке.
