💢 недавно столкнулся с неприятной проблемой при работе с S3
Но для начала вкратце расскажу о том что же такое S3.
S3 (Simple Storage Service) три Эс если по-русски - это объектное хранилище (НЕ ФАЙЛОВАЯ СИСТЕМА)
и облачный сервис изначально разработанный Amazon Web Services (AWS).
Но фраза S3 уже стала нарицательной и я заметил что так называют хранилища даже не относящиеся к амазону и не использующие его сервис.
Строго говоря вся проблема этой путаницы в закрытом исходном коде, - умные американцы не хотят раскрывать свой код, что стабильно приносит очень хорошие деньги, но технология классная поэтому появляются клоны вроде Nimbus, Fake S3, HPE Helion Eucalyptus и Scality S3.
С одним из таких клонов я и работал, для простоты и я продолжу называть его S3.
Итак S3 позволяет пользователям загружать и отправлять данные ЛЮБОГО типа по вебу.
S3 очень ценят за его низкую (в рамках большого бизнеса) цену,
его используют для бэкапов, контент платформ, CDNов, сохранений данных high performance приложений, и конечно для Биг Даты.
Так же считается что под капотом Яндекс Диска и Гугл диска некие свои аналоги aws S3.
С какой же именно проблемой я столкнулся и почему она была неприятной:
В ПРОМ таблице перестали отображаться данные что загружались airflow дагом.
быстрое обращение к s3 с нужным префиксом показало что там этих файлов нет
отправитель файлов в s3 показал свои логи согласно которым файлы были точно успешно отправлены
спрашивается где файлы?
в общем я начал дебажить и обнаружил что при прямом обращении к файлам из логов они находятся
и встал вопрос где же произошла проблема.
в эйрфлоу даге все горит зеленым
контроллер отправляющий в s3 так же говорит что все было успешно отправлено
в обшем, после отправления самых разных вариантов запросов к s3 я все же смог обнаружить эти файлы так
почему же не исходный способ не показал эти файлы?
избавлю вас от мучений - все дело в методе list_objects_v2 и в bucket.objects
всегда помогает чтение доки и помогло мне при решении этой проблемы
https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/s3/client/list_objects_v2.html
чтение доки подсказало что метод list_objects_v2() не может вернуть больше 1000 объектов как не старайся
MaxKeys (integer) – Sets the maximum number of keys returned in the response.
By default, the action returns up to 1,000 key names.
The response might contain fewer keys but will never contain more.
и с моим префиксом накопилось уже больше 1000 объектов
следовательно он возвращал только старые хотя новые уже были готовы и лежали рядом
Ситуацию осложняло то что мы не удаляем данные из s3 а только читаем
то есть удаляй я файлы после чтения - этой ситуации не возникло бы в принципе и я не узнал бы об этой особенности.
Но для начала вкратце расскажу о том что же такое S3.
S3 (Simple Storage Service) три Эс если по-русски - это объектное хранилище (НЕ ФАЙЛОВАЯ СИСТЕМА)
и облачный сервис изначально разработанный Amazon Web Services (AWS).
Но фраза S3 уже стала нарицательной и я заметил что так называют хранилища даже не относящиеся к амазону и не использующие его сервис.
Строго говоря вся проблема этой путаницы в закрытом исходном коде, - умные американцы не хотят раскрывать свой код, что стабильно приносит очень хорошие деньги, но технология классная поэтому появляются клоны вроде Nimbus, Fake S3, HPE Helion Eucalyptus и Scality S3.
С одним из таких клонов я и работал, для простоты и я продолжу называть его S3.
Итак S3 позволяет пользователям загружать и отправлять данные ЛЮБОГО типа по вебу.
S3 очень ценят за его низкую (в рамках большого бизнеса) цену,
его используют для бэкапов, контент платформ, CDNов, сохранений данных high performance приложений, и конечно для Биг Даты.
Так же считается что под капотом Яндекс Диска и Гугл диска некие свои аналоги aws S3.
С какой же именно проблемой я столкнулся и почему она была неприятной:
В ПРОМ таблице перестали отображаться данные что загружались airflow дагом.
быстрое обращение к s3 с нужным префиксом показало что там этих файлов нет
отправитель файлов в s3 показал свои логи согласно которым файлы были точно успешно отправлены
спрашивается где файлы?
в общем я начал дебажить и обнаружил что при прямом обращении к файлам из логов они находятся
и встал вопрос где же произошла проблема.
в эйрфлоу даге все горит зеленым
контроллер отправляющий в s3 так же говорит что все было успешно отправлено
в обшем, после отправления самых разных вариантов запросов к s3 я все же смог обнаружить эти файлы так
import boto3
S3_ENDPOINT = 'fake-data'
S3_BUCKET = 'fake-data'
S3_REGION = 'fake-data'
S3_ACCESS_KEY_ID = 'fake-data'
S3_SECRET_ACCESS_KEY = 'fake-data'
PREFIX_TO_ACCESS = 'mybucket/fake-data/'
sesssion = boto3.Session(S3_ACCESS_KEY_ID, S3_SECRET_ACCESS_KEY)
s3 = sesssion.resource('s3', endpoint_url=S3_ENDPOINT)
bucketName = S3_BUCKET
bucket = s3.Bucket(bucketName)
list_of_lost_files = []
for obj in bucket.objects.filter(Prefix=PREFIX_TO_ACCESS):
list_of_lost_files.append(obj.key)
print(list_of_lost_files)
почему же не исходный способ не показал эти файлы?
import boto3
headers = {'Content-Type': 'application/sql'}
S3_ENDPOINT = 'fake-data'
S3_BUCKET = 'fake-data'
S3_REGION = 'fake-data'
S3_ACCESS_KEY_ID = 'fake-data'
S3_SECRET_ACCESS_KEY = 'fake-data'
PREFIX_TO_ACCESS = 'mybucket/fake-data/'
session = requests.Session()
s3_client = boto3.client(
service_name='s3',
endpoint_url=S3_ENDPOINT,
region_name=S3_REGION,
aws_access_key_id=S3_ACCESS_KEY_ID,
aws_secret_access_key=S3_SECRET_ACCESS_KEY,
)
response = s3_client.list_objects_v2(
Bucket=S3_BUCKET,
Prefix=PREFIX_TO_ACCESS)
for obj in response.get('Contents', []):
print('\n')
print(obj['Key'])
print(obj['LastModified'])
избавлю вас от мучений - все дело в методе list_objects_v2 и в bucket.objects
всегда помогает чтение доки и помогло мне при решении этой проблемы
https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/s3/client/list_objects_v2.html
чтение доки подсказало что метод list_objects_v2() не может вернуть больше 1000 объектов как не старайся
MaxKeys (integer) – Sets the maximum number of keys returned in the response.
By default, the action returns up to 1,000 key names.
The response might contain fewer keys but will never contain more.
и с моим префиксом накопилось уже больше 1000 объектов
следовательно он возвращал только старые хотя новые уже были готовы и лежали рядом
Ситуацию осложняло то что мы не удаляем данные из s3 а только читаем
то есть удаляй я файлы после чтения - этой ситуации не возникло бы в принципе и я не узнал бы об этой особенности.
👍7❤3😭3🔥1
🦠 Услышал такую интересную фразу от коллеги из другого отдела
«Код вы пишите один раз - а читают его потом много раз, вы в том числе. Поэтому всегда лучше писать его сразу как следует»
Вам может показаться это душными понятиями каких то старых дедушек, но с течением времени я понял насколько это важно.
Вот мой личный топ АНТИ ПАТТЕРНОВ в разработке:
1) В самом начале я имел неосторожность использовать ключевые слова в названии переменных, думаю тут не стоит что то еще говорить - ваша программа будет просто вести себя нестабильно если вы будете использовать ключевые слова как переменные. в моем случае я использовал слово type в Python2
2) Писать огромные функции с большим числом аргументов. Их так же сложно читать и дебажить, в целом есть даже такое правило что в названии вашей функции не должно быть слов вроде and or with итд - то есть нужна строгая модульность: одна функция выполняет одну задачу
так же есть правило не больше пяти аргументов на функцию.
3) Не писать юнит тесты - тут все просто при добавлении нового функционала вам не придется как обезьянке заново все проверять вручную теряя время. Что гипер важно.
4) Не использовать стандарты языка (в Python этот стандарты PEP) они унифицируют то какие отступы нужно делать между импортами, как называть и не называть переменные, как форматировать строки и многое многое другое
5) применять очень большие try/except конструкции - при особых условиях из за них можно просто потерять ошибку в логах, а так же это крайне неудобно дебажить.
6) использовать непонятные переменные вроде i,j,a,b итд. Вам будет неудобно в будущем вникать что же они значат и например по чему же предполагается тут итерироваться.
«Код вы пишите один раз - а читают его потом много раз, вы в том числе. Поэтому всегда лучше писать его сразу как следует»
Вам может показаться это душными понятиями каких то старых дедушек, но с течением времени я понял насколько это важно.
Вот мой личный топ АНТИ ПАТТЕРНОВ в разработке:
1) В самом начале я имел неосторожность использовать ключевые слова в названии переменных, думаю тут не стоит что то еще говорить - ваша программа будет просто вести себя нестабильно если вы будете использовать ключевые слова как переменные. в моем случае я использовал слово type в Python2
2) Писать огромные функции с большим числом аргументов. Их так же сложно читать и дебажить, в целом есть даже такое правило что в названии вашей функции не должно быть слов вроде and or with итд - то есть нужна строгая модульность: одна функция выполняет одну задачу
так же есть правило не больше пяти аргументов на функцию.
3) Не писать юнит тесты - тут все просто при добавлении нового функционала вам не придется как обезьянке заново все проверять вручную теряя время. Что гипер важно.
4) Не использовать стандарты языка (в Python этот стандарты PEP) они унифицируют то какие отступы нужно делать между импортами, как называть и не называть переменные, как форматировать строки и многое многое другое
5) применять очень большие try/except конструкции - при особых условиях из за них можно просто потерять ошибку в логах, а так же это крайне неудобно дебажить.
6) использовать непонятные переменные вроде i,j,a,b итд. Вам будет неудобно в будущем вникать что же они значат и например по чему же предполагается тут итерироваться.
🔥8👍2💯2👌1🌚1
Поговорим о так называемой БАЗЕ - *Медленно меняющиеся измерения (Slowly Changing Dimensions)* - ЭсКаДэ - это детище методолгии Кимбалла.
по сути это таблицы, в которых некоторые атрибуты могут изменить свои значения по истечении некоторого периода времени, причем частота таких изменений является небольшой.
Интересно это с точки зрения организации версионирования данных в вашей таблице.
SCD 0
данные после первого попадания в таблицу далее никогда не изменяются. Нужен лишь как нулевая точка отсчета для методологии SCD. По сути, вообще не SCD.
SCD 1
это обычная перезапись старых данных новыми. В чистом виде этот метод тоже не содержит версионности и используется лишь там, где история фактически не нужна
SCD 2
для каждой версии создается отдельная запись в таблице с добавлением поля-ключевого атрибута данной версии.
SCD 3
В самой записи содержатся дополнительные поля для предыдущих значений атрибута. При получении новых данных, старые данные перезаписываются текущими значениями.
SCD 4
История изменений содержится в отдельной таблице измерении: основная таблица всегда перезаписывается текущими данными с перенесением старых данных в другую таблицу. Обычно этот тип используют для аудита изменений или создания архивных таблиц.
SCD 5
Методика SCD 5 основана на мини-измерении SCD 4 путем внедрения ключа мини-измерения «текущий профиль» в базовое измерение, которое перезаписывается как атрибут SCD 1. Поэтому в общем то SCD 5 (4+1), это позволяет получить доступ к назначенным в данный момент значениям мини-измерения вместе с другими значениями базового измерения без привязки через таблицу фактов.
SCD 6
Комбинация 1, 2 и 3 методов и предназначен для ситуаций, которые они не учитывают или для большего удобства работы с данными.
SCD 7
это другой способ достижения того же, что и SCD 6, где вы поддерживаете версию значений SCD 1 отдельно от версии значений SCD 2. Часто версия SCD 1 создается с использованием представления SCD 2. Имея оба ключа в факте, вы можете узнать, как обстояли дела на момент факта, а также как все обстояло на основе текущих версий измерений. Это позволяет избежать необходимости обновлять старые значения в соответствии с текущим состоянием.
по сути это таблицы, в которых некоторые атрибуты могут изменить свои значения по истечении некоторого периода времени, причем частота таких изменений является небольшой.
Интересно это с точки зрения организации версионирования данных в вашей таблице.
SCD 0
данные после первого попадания в таблицу далее никогда не изменяются. Нужен лишь как нулевая точка отсчета для методологии SCD. По сути, вообще не SCD.
SCD 1
это обычная перезапись старых данных новыми. В чистом виде этот метод тоже не содержит версионности и используется лишь там, где история фактически не нужна
SCD 2
для каждой версии создается отдельная запись в таблице с добавлением поля-ключевого атрибута данной версии.
SCD 3
В самой записи содержатся дополнительные поля для предыдущих значений атрибута. При получении новых данных, старые данные перезаписываются текущими значениями.
SCD 4
История изменений содержится в отдельной таблице измерении: основная таблица всегда перезаписывается текущими данными с перенесением старых данных в другую таблицу. Обычно этот тип используют для аудита изменений или создания архивных таблиц.
SCD 5
Методика SCD 5 основана на мини-измерении SCD 4 путем внедрения ключа мини-измерения «текущий профиль» в базовое измерение, которое перезаписывается как атрибут SCD 1. Поэтому в общем то SCD 5 (4+1), это позволяет получить доступ к назначенным в данный момент значениям мини-измерения вместе с другими значениями базового измерения без привязки через таблицу фактов.
SCD 6
Комбинация 1, 2 и 3 методов и предназначен для ситуаций, которые они не учитывают или для большего удобства работы с данными.
SCD 7
это другой способ достижения того же, что и SCD 6, где вы поддерживаете версию значений SCD 1 отдельно от версии значений SCD 2. Часто версия SCD 1 создается с использованием представления SCD 2. Имея оба ключа в факте, вы можете узнать, как обстояли дела на момент факта, а также как все обстояло на основе текущих версий измерений. Это позволяет избежать необходимости обновлять старые значения в соответствии с текущим состоянием.
❤7🔥3😍1
Расскажу об особенностях использования CRONa. 🕰️
Иногда бывает что нужно запускать какой то скрипт регулярно, а полноценные ETL фреймворки использовать невозможно/слишком затратно итп. 🌚
Итак что нужно сделать чтобы использовать утилиту CRON на Linux.
Для начала положить ваш скрипт/джарник/башник на машину где вы предполагаете его запускать.
Сделать этот скрипт исполняемым. Например так
Убедиться в том что пути(если они есть) в вашем скрипе абсолютные.
Что это значит?
Компьютер не знает о ваших локальных файловых системах и не будет запускаться из под папки юзеров где вы скорее всего отлаживали свой код - а значит нужно указать везде абсолютные пути если они есть.
Еще лучше это делать программмно. Используя стандартный модуль os или sys в случае Python.
Важный момент! ‼️
Если вы подменяете файл не забывать его так же делать исполняемым.
При подкладывании другого файлу и переименовании меняется номер инода - соответсвенно для компьютера это совершенно другой файл и вы можете столкнуться с проблемой при запуске тк прав 777 у него уже нет (777 - максимальный доступ к файлу для всех пользователей и групп пользователей)
Про то как именно настраивать время запуска CRON - можно почитать здесь.
https://crontab.guru/every-1-minute
Иногда бывает что нужно запускать какой то скрипт регулярно, а полноценные ETL фреймворки использовать невозможно/слишком затратно итп. 🌚
Итак что нужно сделать чтобы использовать утилиту CRON на Linux.
Для начала положить ваш скрипт/джарник/башник на машину где вы предполагаете его запускать.
Сделать этот скрипт исполняемым. Например так
Chmod -x my_noscript.sh
Chmod 777 my_file.py
Убедиться в том что пути(если они есть) в вашем скрипе абсолютные.
Что это значит?
Компьютер не знает о ваших локальных файловых системах и не будет запускаться из под папки юзеров где вы скорее всего отлаживали свой код - а значит нужно указать везде абсолютные пути если они есть.
Еще лучше это делать программмно. Используя стандартный модуль os или sys в случае Python.
Важный момент! ‼️
Если вы подменяете файл не забывать его так же делать исполняемым.
При подкладывании другого файлу и переименовании меняется номер инода - соответсвенно для компьютера это совершенно другой файл и вы можете столкнуться с проблемой при запуске тк прав 777 у него уже нет (777 - максимальный доступ к файлу для всех пользователей и групп пользователей)
Про то как именно настраивать время запуска CRON - можно почитать здесь.
https://crontab.guru/every-1-minute
crontab.guru
Crontab.guru - The cron schedule expression generator
An easy to use editor for crontab schedules.
👍8🔥4❤🔥3
Итак что же здесь происходит? 🤯
На самом деле тут вступают в дело несколько особенностей языка python.
Во первых
хоть на первый взгляд кажется, что функция print_list и была объявлена раньше чем был создан лист new_list - это не мешает коду быть корректно выполненным.
До непосредственного вызова функции print_list - python просто создает привязку имени функции. Никакой код не выполняется, кроме анализа синтаксиса.
Обратите внимание, что это не похоже на PHP или JavaScript, где функции 'поднимаются' -т.е. любые определения функций обрабатываются и анализируются раньше всего остального. В PHP и JavaScript совершенно законно определять функции в коде ниже, чем то место, где они вызываются.
Таким образом, синтаксический анализатор не дошел до выполнения print_list() пока его непосредственно не вызвали и соответственно не упал с NameError: name 'new_list' is not defined. Более того - если бы мы не вызвали в конце эту функцию print_list() и понаписали в нее еще больше несуществующих листов вроде этого😬
во вторых - уже про область видимости. new_list находится в глобальной области видимости, то есть изнутри функции print_list мы можем к нему обращаться, поскольку из локальной области видимости видно то, что происходит в глобальной области видимости (но очевидно не наоборот).
На самом деле тут вступают в дело несколько особенностей языка python.
Во первых
хоть на первый взгляд кажется, что функция print_list и была объявлена раньше чем был создан лист new_list - это не мешает коду быть корректно выполненным.
До непосредственного вызова функции print_list - python просто создает привязку имени функции. Никакой код не выполняется, кроме анализа синтаксиса.
Обратите внимание, что это не похоже на PHP или JavaScript, где функции 'поднимаются' -т.е. любые определения функций обрабатываются и анализируются раньше всего остального. В PHP и JavaScript совершенно законно определять функции в коде ниже, чем то место, где они вызываются.
Таким образом, синтаксический анализатор не дошел до выполнения print_list() пока его непосредственно не вызвали и соответственно не упал с NameError: name 'new_list' is not defined. Более того - если бы мы не вызвали в конце эту функцию print_list() и понаписали в нее еще больше несуществующих листов вроде этого😬
def print_list(some_list):то никакой ошибки бы так же не было, поскольку функция не была вызвана. Вызов функции - это обходной путь в потоке выполнения программы. Вместо перехода к следующему куску кода, поток переходит к телу функции, выполняет там все операторы, а затем возвращается, чтобы продолжить с того места, где он остановился.
for element in sudden_list:
print(element)
for element in mega_list:
print(element)
for element in super_list:
print(element)
print('Hello')
во вторых - уже про область видимости. new_list находится в глобальной области видимости, то есть изнутри функции print_list мы можем к нему обращаться, поскольку из локальной области видимости видно то, что происходит в глобальной области видимости (но очевидно не наоборот).
❤7✍4
😡😡😡 Антитоп Антипаттернов при написании кода в Питоне
Кое что вобрал от своих коллег, к чему то пришел сам:
итак
1) Переменные названы непонятно для стороннего человека или не до конца объясняют суть переменной
2) Неиспользуемые переменные остаются в коде
3) «Магические» числа в коде
4) Переменные названы неконсистентно
5) Функция слишком длинная и принимает 5 и более аргументов
6) Функция выполняет несколько дел:
7) Используется булевый флаг для изменения поведения функции
8) Такие артефакты в коде
9) Не используем названия аргументов при вызове функции
10) Проверяем на правильность, а не "пробуем" выполнить код с полученными данными
11) Не используем менеджер контекстов
12) Комментируем слишком подробно или не комментируем совсем
Кое что вобрал от своих коллег, к чему то пришел сам:
итак
1) Переменные названы непонятно для стороннего человека или не до конца объясняют суть переменной
hrs = 5
weeks = 3
for p in products:
print(p)
2) Неиспользуемые переменные остаются в коде
for i, product in enumerate(products):
print(i)
3) «Магические» числа в коде
for I in range(24):
4) Переменные названы неконсистентно
kids_card =''
det_union_month=''
5) Функция слишком длинная и принимает 5 и более аргументов
6) Функция выполняет несколько дел:
def create_and_push():
7) Используется булевый флаг для изменения поведения функции
def round_number(value: float, up: bool) -> float:
8) Такие артефакты в коде
def hello_name(name):
print(name)
9) Не используем названия аргументов при вызове функции
tournament_search("Chess", False, 2023, True)
10) Проверяем на правильность, а не "пробуем" выполнить код с полученными данными
if not os.path.isfile(filename):
print('File does not exist.')
if not os.access(filename, os.W_OK):
print('You dont have write permission.')
with open(filename, 'w') as file:
file.write('Hello, World!')
return(f"Hello {name}")
11) Не используем менеджер контекстов
file = open(file_path, 'w')
file.write('Hello, World!')
file.close()
12) Комментируем слишком подробно или не комментируем совсем
👍9🤬2🔥1
😎😎😎 А теперь поговорим как раз таки о лучших практиках при написании кода на Питоне:
1) Переменные названы понятно любому, кто взглянет на код. Даже длинные названия приемлемы в угоду понятности.
2) Неиспользуемые переменные заменяются на _
3) Переменные названы по определенному принципу
4) Нет «магических» чисел, но есть переменные с нормальным названием
5) Функция нормального размера - 40 строчек кода в одной функции MAX
6) Функция выполняет только одно дело:
7) Создаем две функции вместо булевого флага (вспоминаем функцию round_number из прошлого поста)
8) Создаем и используем класс, вместо кучи аргументов функции
9) Используем аннотации типов при определении функции
10) Используем названия аргументов при вызове функции
11) Пробуем и выдаем ошибку вместо попытки включить все возможные проверки на правильность
12) Используем менеджер контекстов
1) Переменные названы понятно любому, кто взглянет на код. Даже длинные названия приемлемы в угоду понятности.
hours_elapsed = 5
weeks_when_data_is_valid = 3
for product in products:
2) Неиспользуемые переменные заменяются на _
for i, _ in enumerate(products):
print(i)
3) Переменные названы по определенному принципу
kids_card
kids_union_month
4) Нет «магических» чисел, но есть переменные с нормальным названием
hours_in_day = 24
for I in range(hours_in_day):
5) Функция нормального размера - 40 строчек кода в одной функции MAX
6) Функция выполняет только одно дело:
def create():
...
def push():
...
7) Создаем две функции вместо булевого флага (вспоминаем функцию round_number из прошлого поста)
def round_up(value: float) -> float:
...
def round_down(value: float) -> float:
...
8) Создаем и используем класс, вместо кучи аргументов функции
class BlogPost:
def __init__(self, noscript, author, created_timestamp,
updated_timestamp, content):
…
blog_post1 = BlogPost(…)
def render_blog_post(blog_post):
….
render_blog_post(blog_post1)
9) Используем аннотации типов при определении функции
def hello_name(name: str) -> str:
return(f"Hello {name}")
10) Используем названия аргументов при вызове функции
tournament_search(game="Chess", include_private=False, year=2023, popular=True)
11) Пробуем и выдаем ошибку вместо попытки включить все возможные проверки на правильность
try:
with open(filename, 'w') as file:
file.write('Hello, World!')
except FileNotFoundError:
print('File does not exist.')
except PermissionError:
print('You dont have write permission.')
except OSError as exc:
print(f'An OSError has occurred:\n{exc}')
12) Используем менеджер контекстов
with open(file_path, 'w') as file:
file.write('Hello, World!')
🔥13👍2👏1
🗑️ Эти вопросы всегда задают при собесах на должности связанные с Python. 🗑️
Как работает garbage collector и зачем он нужен? Какие механизмы очистки мусора существуют?
Python разработчики не заботятся о таких вещах, как освобождение и выделение памяти, что свойственно для разработки на C++ и C - эту проблему решает garbage collector.
Как только созданные объекты больше не нужны, сборщик мусора автоматически освободит память из-под них.
CPython (стандартный интерпретатор Python) использует два механизма для сборки мусора - подсчет ссылок на объекты и generational garbage collector (модуль gc в стандартной библиотеке Python).
В Python все переменные являются ссылками на объекты. Естественно, на один объект может ссылаться несколько переменных. Каждый раз, когда счетчик ссылок становится равным нулю, запускается механизм уничтожения объекта, при этом удаляются и ссылки, которые этот объект имел к другим объектам, таким образом, уничтожение одного объекта может повлечь большую волну удалений других объектов.
В Python с помощью функции sys.getrefcount() можно узнать количество ссылок на объект.
Результат
2
3
В примере создается список и на него ссылается переменная test_list, количество ссылок равно 1. Когда применяется функция sys.getrefcount(), количество ссылок возрастает до 2. Затем мы создаем еще одну переменную test_list_2 и присваиваем ей переменную test_list, счетчик ссылок равен 2, а при повторном применении getrefcount() становится равным 3.
Функция sys.getrefcount() обычно возвращает количество ссылок, большее на единицу, чем ожидалось.
Это связано с созданием временной ссылки на аргумент, который передается в функцию.
Но вот в чем нюанс, - алгоритм подсчета ссылок, не может разрешить циклические зависимости. Эту проблему призван решить generational garbage collector (GGC).
Циклическая ссылка - это когда один или несколько объектов ссылаются друг на друга.
Здесь список ссылается сам на себя.
Если вызвать метод del(), то произойдет удаление ссылок на объекты. Если бы не было GGC, то объекты так бы и остались висеть в памяти, хотя и были бы недоступны для разработчика. (Нечто подобное происходит в спарке если неправильно почистить кэш см. посты выше)
В отличие от подсчета ссылок, GGC не срабатывает в реальном времени и запускается периодически. Для определения частоты запусков есть особая встроенная логика.
Generational означает «относящийся к определенному поколению» (generation - поколение), в GGC все объекты разделяет на три поколения. Изначально все объекты помещаются в первое поколение, живут там некоторое время, и большинство из них очищается, остальная часть перемещается во второе поколение и потом в третье. Чем выше поколение, тем реже оно сканируется на мусор.
Для выявления циклических ссылок GGC итерирует каждый объект в поколении и временно удаляет все ссылки, на которые этот объект ссылается. После полного обхода все объекты, у которых счетчик ссылок меньше двух, считаются недоступными и удаляются.
GGC как модуль несет для разработчика возможность управлять сборщиком мусора для циклических ссылок при необходимости. его можно вызвать написав import gc.
Как работает garbage collector и зачем он нужен? Какие механизмы очистки мусора существуют?
Python разработчики не заботятся о таких вещах, как освобождение и выделение памяти, что свойственно для разработки на C++ и C - эту проблему решает garbage collector.
Как только созданные объекты больше не нужны, сборщик мусора автоматически освободит память из-под них.
CPython (стандартный интерпретатор Python) использует два механизма для сборки мусора - подсчет ссылок на объекты и generational garbage collector (модуль gc в стандартной библиотеке Python).
В Python все переменные являются ссылками на объекты. Естественно, на один объект может ссылаться несколько переменных. Каждый раз, когда счетчик ссылок становится равным нулю, запускается механизм уничтожения объекта, при этом удаляются и ссылки, которые этот объект имел к другим объектам, таким образом, уничтожение одного объекта может повлечь большую волну удалений других объектов.
В Python с помощью функции sys.getrefcount() можно узнать количество ссылок на объект.
import sys
test_list = [1,2,3]
print(sys.getrefcount(test_list))
test_list_2 = test_list
print(sys.getrefcount(test_list_2))
Результат
2
3
В примере создается список и на него ссылается переменная test_list, количество ссылок равно 1. Когда применяется функция sys.getrefcount(), количество ссылок возрастает до 2. Затем мы создаем еще одну переменную test_list_2 и присваиваем ей переменную test_list, счетчик ссылок равен 2, а при повторном применении getrefcount() становится равным 3.
Функция sys.getrefcount() обычно возвращает количество ссылок, большее на единицу, чем ожидалось.
Это связано с созданием временной ссылки на аргумент, который передается в функцию.
Но вот в чем нюанс, - алгоритм подсчета ссылок, не может разрешить циклические зависимости. Эту проблему призван решить generational garbage collector (GGC).
Циклическая ссылка - это когда один или несколько объектов ссылаются друг на друга.
a = []
a.append(a)
Здесь список ссылается сам на себя.
Если вызвать метод del(), то произойдет удаление ссылок на объекты. Если бы не было GGC, то объекты так бы и остались висеть в памяти, хотя и были бы недоступны для разработчика. (Нечто подобное происходит в спарке если неправильно почистить кэш см. посты выше)
В отличие от подсчета ссылок, GGC не срабатывает в реальном времени и запускается периодически. Для определения частоты запусков есть особая встроенная логика.
Generational означает «относящийся к определенному поколению» (generation - поколение), в GGC все объекты разделяет на три поколения. Изначально все объекты помещаются в первое поколение, живут там некоторое время, и большинство из них очищается, остальная часть перемещается во второе поколение и потом в третье. Чем выше поколение, тем реже оно сканируется на мусор.
Для выявления циклических ссылок GGC итерирует каждый объект в поколении и временно удаляет все ссылки, на которые этот объект ссылается. После полного обхода все объекты, у которых счетчик ссылок меньше двух, считаются недоступными и удаляются.
GGC как модуль несет для разработчика возможность управлять сборщиком мусора для циклических ссылок при необходимости. его можно вызвать написав import gc.
👍7✍3🔥1
🚑 Поговорим о таком явлении как mangling в Python. (от англ. mangle - ломать, серьезно повреждать, калечить) в русском коммьюнити это известно как "искажение" (звучит не так запоминающееся по мне)
Эта особенность языка позволяющая обращаться к приватным методам и атрибутам, в обход условных ограничений.
Но все по порядку:
В Python можно выделить три уровня доступа данным:
public - доступно всем;
private - доступно только внутри класса;
protected - доступно внутри класса и внутри классов-наследников.
Уровень public - это уровень по умолчанию, не нужно использовать особые конструкции, чтобы сделать атрибут или метод публичным.
Чтобы сделать атрибут или метод protected, нужно добавить один символ _ перед названием
Чтобы сделать атрибут или метод private, нужно добавить два символа __ перед названием:
и казалось бы на этом все, в языке работает стандартная инкапсуляция, и к этим данным не обратиться.
Но нет, хоть при прямом доступе и произойдет ошибка, в Python регулирование инкапсуляции - это лишь условность и договоренность между разработчиками. Когда один разработчик видит _ и __ в атрибуте или методе, то будучи порядочным человеком - он понимает, что другой разработчик так назвал их желая "скрыть" их от прямого использования, а значит за этим была какая то логика и смысл, которые не стоит нарушать.
теперь возвращаемся к явлению mangling - "калеча" исходное название класса можно обращаться к приватным атрибутам и методам как в примере ниже:
однако если бы обратились напрямую:
bank_card.__get_pin()
bank_card.__serial_number
то получили бы ошибку
Traceback (most recent call last):
File "main.py", line 8, in <module>
bank_card.__get_pin()
AttributeError: 'BankCard' object has no attribute '__get_pin'
еще интересно посмотреть на список всех атрибутов нового объекта,
print(dir(bank_card))
получим:
вот они с лева на право:
_BankCard__get_pin
_BankCard__pin
_BankCard__serial_number
Это относится к вредным советам и особенностям языка, лично мне никогда не приходилось с этим сталкиваться и использовать в работе - но я считаю, что знать такое полезно.
теоретически это можно использовать при наследовании, тк при стандартном сценарии - производный класс затрет родительский метод - однако с помощью mangling это можно обойти сделав оба метода приватными.
Эта особенность языка позволяющая обращаться к приватным методам и атрибутам, в обход условных ограничений.
Но все по порядку:
В Python можно выделить три уровня доступа данным:
public - доступно всем;
private - доступно только внутри класса;
protected - доступно внутри класса и внутри классов-наследников.
Уровень public - это уровень по умолчанию, не нужно использовать особые конструкции, чтобы сделать атрибут или метод публичным.
Чтобы сделать атрибут или метод protected, нужно добавить один символ _ перед названием
Чтобы сделать атрибут или метод private, нужно добавить два символа __ перед названием:
и казалось бы на этом все, в языке работает стандартная инкапсуляция, и к этим данным не обратиться.
Но нет, хоть при прямом доступе и произойдет ошибка, в Python регулирование инкапсуляции - это лишь условность и договоренность между разработчиками. Когда один разработчик видит _ и __ в атрибуте или методе, то будучи порядочным человеком - он понимает, что другой разработчик так назвал их желая "скрыть" их от прямого использования, а значит за этим была какая то логика и смысл, которые не стоит нарушать.
теперь возвращаемся к явлению mangling - "калеча" исходное название класса можно обращаться к приватным атрибутам и методам как в примере ниже:
class BankCard:
__serial_number = "1111 2222 3333 4444" # private-переменная
__pin = 955 # private-переменная
def __get_pin(self): # private-метод
print( "My pin is : ", self.__pin)
bank_card = BankCard()
bank_card._BankCard__get_pin()
bank_card._BankCard__serial_number = "2222 3333 4444 66666"
print( "New serial number is ", bank_card._BankCard__serial_number )
однако если бы обратились напрямую:
bank_card.__get_pin()
bank_card.__serial_number
то получили бы ошибку
Traceback (most recent call last):
File "main.py", line 8, in <module>
bank_card.__get_pin()
AttributeError: 'BankCard' object has no attribute '__get_pin'
еще интересно посмотреть на список всех атрибутов нового объекта,
print(dir(bank_card))
получим:
['_BankCard__get_pin', '_BankCard__pin', '_BankCard__serial_number', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__']
вот они с лева на право:
_BankCard__get_pin
_BankCard__pin
_BankCard__serial_number
Это относится к вредным советам и особенностям языка, лично мне никогда не приходилось с этим сталкиваться и использовать в работе - но я считаю, что знать такое полезно.
теоретически это можно использовать при наследовании, тк при стандартном сценарии - производный класс затрет родительский метод - однако с помощью mangling это можно обойти сделав оба метода приватными.
class baseClass:
def __myFunc(self):
print("I'm a parent.")
class derivedClass(baseClass):
def __myFunc(self):
print("I'm a child.")
obj=derivedClass()
obj._derivedClass__myFunc()
obj._baseClass__myFunc()
🔥8⚡4😢1🆒1
🕹Хочу рассказать об очень интересной браузерной игре в которой можно хорошо научиться пользоваться линуксом.
Игра называется "Terminus", разработана в одном из самых уважаемых университетов мира в сфере информационных технологий (MIT - massachusetts institute of technology)
Выходцы из этого университета, университета Беркли и некоторых других фактически положили основу всему современному IT, на котором стою в том числе и я (Например знаменитый Hadoop был разработан выходцем из MIT, протокол Kerberos который я до сих пор использую в работе - тоже).
https://web.mit.edu/mprat/Public/web/Terminus/Web/main.html
Лично мне в свое время эта игра помога научиться пользоваться линуксом на базовом уровне. Смело могу рекомендовать ее и вам.
Игра называется "Terminus", разработана в одном из самых уважаемых университетов мира в сфере информационных технологий (MIT - massachusetts institute of technology)
Выходцы из этого университета, университета Беркли и некоторых других фактически положили основу всему современному IT, на котором стою в том числе и я (Например знаменитый Hadoop был разработан выходцем из MIT, протокол Kerberos который я до сих пор использую в работе - тоже).
https://web.mit.edu/mprat/Public/web/Terminus/Web/main.html
Лично мне в свое время эта игра помога научиться пользоваться линуксом на базовом уровне. Смело могу рекомендовать ее и вам.
web.mit.edu
Terminus
🔥5⚡1👨💻1
DataSkewer
🗑️ Эти вопросы всегда задают при собесах на должности связанные с Python. 🗑️ Как работает garbage collector и зачем он нужен? Какие механизмы очистки мусора существуют? Python разработчики не заботятся о таких вещах, как освобождение и выделение памяти,…
Разберем фундаментальную, и довольно сложную тему в Python - GIL ⚒⚙️🔩
но перед этим нужно вспомнить немного теории
CPU-bound программы (обработка изображений, умножение матриц, поиск)
I/O-bound программы (связь по сети, обращение к БД, ожидание ввода данных от пользователя)
Процесс - это часть виртуальной памяти и ресурсов, которые операционная система выделяет для выполнения программы.
Каждый процесс выполняется в отдельном адресном пространстве и не может получить доступ к ресурсам другого процесса.
У каждой программы есть как минимум один процесс, а у каждого процесса - минимум один поток, который называют главным.
Разница между потоками и процессами состоит в том, что потоки используют память, выделенную под процесс и обращаются к ресурсам друг друга напрямую, а процессы, в свою очередь, обмениваются данными между собой только с помощью механизмов межпроцессного взаимодействия.
Поток - это отдельная «нить» (поток) выполнения внутри процесса.
ИТАК
В Python и его стандартном интерпретаторе CPython программы выполняются последовательно. Это связано с GIL (Global Interpreter Lock), который ограничивает Python на один запущенный поток в единицу времени в рамках одного процесса. 😢😢😢
(В Go и С++ например это не проблема и там позволительно иметь несколько потоков сразу)
В посте на который я отвечаю, было сказано, что в Python существует система подсчета ссылок на объекты. Проблема, которую решает GIL, связана с возможностью одновременного увеличения или уменьшения ссылок на объекты разными потоками
т.е. может возникнуть ситуация, когда один поток уменьшит число ссылок на объект и Python удалит его, а другой поток будет использовать только что удаленный объект, что приведет к падению.
Но все не так плохо, многопоточность тем не менее можно эффективно реализовать, каждый раз когда поток вынужден ждать какую либо Input/output операцию, - другие потоки получают шанс получить время на выполнение (это все в происходит за ничтожно маленькое по меркам человека время) и между потоками происходит переключение GIL.
И соответственно для таких задач (I/O bound) многопоточность в Python остается хорошим вариантом.
Но даже для CPU-bound программ всегда остается мультипроцессинг, который правда сильнее нагружает Операционную систему на которой запущенно приложение.
Еще есть вариант для самых сильных и смелых - написать собственное расширение на C для работы с потоками и использовать его соответственно,
Так же можно использовать другие интерпретаторы такие как Jython и IronPython в которых нет GIL.
но перед этим нужно вспомнить немного теории
CPU-bound программы (обработка изображений, умножение матриц, поиск)
I/O-bound программы (связь по сети, обращение к БД, ожидание ввода данных от пользователя)
Процесс - это часть виртуальной памяти и ресурсов, которые операционная система выделяет для выполнения программы.
Каждый процесс выполняется в отдельном адресном пространстве и не может получить доступ к ресурсам другого процесса.
У каждой программы есть как минимум один процесс, а у каждого процесса - минимум один поток, который называют главным.
Разница между потоками и процессами состоит в том, что потоки используют память, выделенную под процесс и обращаются к ресурсам друг друга напрямую, а процессы, в свою очередь, обмениваются данными между собой только с помощью механизмов межпроцессного взаимодействия.
Поток - это отдельная «нить» (поток) выполнения внутри процесса.
ИТАК
В Python и его стандартном интерпретаторе CPython программы выполняются последовательно. Это связано с GIL (Global Interpreter Lock), который ограничивает Python на один запущенный поток в единицу времени в рамках одного процесса. 😢😢😢
(В Go и С++ например это не проблема и там позволительно иметь несколько потоков сразу)
В посте на который я отвечаю, было сказано, что в Python существует система подсчета ссылок на объекты. Проблема, которую решает GIL, связана с возможностью одновременного увеличения или уменьшения ссылок на объекты разными потоками
т.е. может возникнуть ситуация, когда один поток уменьшит число ссылок на объект и Python удалит его, а другой поток будет использовать только что удаленный объект, что приведет к падению.
Но все не так плохо, многопоточность тем не менее можно эффективно реализовать, каждый раз когда поток вынужден ждать какую либо Input/output операцию, - другие потоки получают шанс получить время на выполнение (это все в происходит за ничтожно маленькое по меркам человека время) и между потоками происходит переключение GIL.
И соответственно для таких задач (I/O bound) многопоточность в Python остается хорошим вариантом.
Но даже для CPU-bound программ всегда остается мультипроцессинг, который правда сильнее нагружает Операционную систему на которой запущенно приложение.
Еще есть вариант для самых сильных и смелых - написать собственное расширение на C для работы с потоками и использовать его соответственно,
Так же можно использовать другие интерпретаторы такие как Jython и IronPython в которых нет GIL.
🔥4
Панчлайн нового логотипа канала в том, что на логотипе Hadoop (один из основных инструментов, что я использую в работе) - так же изображен слон.
Из смешного - как гласит легенда, название и логотип хадупа появились так: сын одного из главных разработчиков играл с плюшевой игрушкой по имени "Хадуп", разработчика это зацепило и вот уже более десяти лет другие разработчики по всему миру смотрят на желтого слона.
Из смешного - как гласит легенда, название и логотип хадупа появились так: сын одного из главных разработчиков играл с плюшевой игрушкой по имени "Хадуп", разработчика это зацепило и вот уже более десяти лет другие разработчики по всему миру смотрят на желтого слона.
👏4😱3🔥2
🪲Вчера я совершил свою первую XSS атаку на станции Positive Technologies на ИТ Пикнике, в следующем посте напишу о самом мероприятии, вкратце - мне понравилось, есть разница с прошлым годом.
Но вернемся к теме, итак - XSS(Cross Site Scripting (в английских аббревиатурах зачастую X имеет значение Cross вместо логичного и очевидного "Икс")) и хочу вам рассказать о том, что же это такое.
Простейшая XSS атака выглядела бы так:
Вы вводите в какое-нибудь промт поле (размещение в поисковой строке, в области обратной связи, месте для сообщений/комментариев) данный код
После попытки сделать импорт изображения из несуществующего источника (стринга х)
сработает обработчик ошибок onerror что и вызовет потенциально вредоносный код, в нашем случае это безобидный поп-ап с текстом "Hello XSS".
Для такого скриптинга используются следующие лазейки в приложениях:
1) Ошибки, сделанные в браузере, например реализация сценария посредством тегов SVG / IMG, при которой появляется возможность не принимать во внимание правило ограниченного домена. Это грубая, заметная ошибка разработчиков, если такая атака пройдет - то сами и виноваты)
2) Проблемы с экранированием. Пока браузеры не научились дифференцировать обыкновенный текст от кода. Для распознавания и реализации команд последнего необходима разметка тегами <noscript></noscript>
3) Замена кодировки в заглавии страницы. Определение кодировок происходит в ходе обработки веба. Она находится в метке <meta>, если <noscript> расположен до нее, то браузер начинает работать с заголовком, после обращает внимание на кодировку. У взломщика появляется шанс обойтись без фильтрации символов <>, “” и поместить в noscript вредоносный скрипт, созданный в формате UTF-7.
4) Межсайтовый скриптинг с использованием SQL-инъекции - смешанный способ атаки с привлечением базы данных в которую и делается инъекция. В страницу базы данных посредством SQL-инъекции внедряется вредоносный код, который потом отдается обратно на фронтенд (скорее всего перед этим злоумышленник будет иметь хорошее представление о системе ).
Простую SQL инъекцию можно сделать вставив текст ниже в какое либо поле. Принцип работы так же основан на плохом/отсутствующем экранировании.
На этом сайте вы можете попробовать поотачивать свои навыки в XSS 😄
Так же стоит отметить, что принцип таких XSS Атак отдаленно похож на принцип работы такого инструмента как Google Tag Manager. Поэтому, рекомендую вам копнуть в эту тему глубже если планируете развиваться как Веб-аналитик.
https://xss-game.appspot.com/
Но вернемся к теме, итак - XSS(Cross Site Scripting (в английских аббревиатурах зачастую X имеет значение Cross вместо логичного и очевидного "Икс")) и хочу вам рассказать о том, что же это такое.
Простейшая XSS атака выглядела бы так:
Вы вводите в какое-нибудь промт поле (размещение в поисковой строке, в области обратной связи, месте для сообщений/комментариев) данный код
<div>
<img src='x' onerror='alert("Hello XSS")'>
</div>
После попытки сделать импорт изображения из несуществующего источника (стринга х)
сработает обработчик ошибок onerror что и вызовет потенциально вредоносный код, в нашем случае это безобидный поп-ап с текстом "Hello XSS".
Для такого скриптинга используются следующие лазейки в приложениях:
1) Ошибки, сделанные в браузере, например реализация сценария посредством тегов SVG / IMG, при которой появляется возможность не принимать во внимание правило ограниченного домена. Это грубая, заметная ошибка разработчиков, если такая атака пройдет - то сами и виноваты)
2) Проблемы с экранированием. Пока браузеры не научились дифференцировать обыкновенный текст от кода. Для распознавания и реализации команд последнего необходима разметка тегами <noscript></noscript>
3) Замена кодировки в заглавии страницы. Определение кодировок происходит в ходе обработки веба. Она находится в метке <meta>, если <noscript> расположен до нее, то браузер начинает работать с заголовком, после обращает внимание на кодировку. У взломщика появляется шанс обойтись без фильтрации символов <>, “” и поместить в noscript вредоносный скрипт, созданный в формате UTF-7.
4) Межсайтовый скриптинг с использованием SQL-инъекции - смешанный способ атаки с привлечением базы данных в которую и делается инъекция. В страницу базы данных посредством SQL-инъекции внедряется вредоносный код, который потом отдается обратно на фронтенд (скорее всего перед этим злоумышленник будет иметь хорошее представление о системе ).
Простую SQL инъекцию можно сделать вставив текст ниже в какое либо поле. Принцип работы так же основан на плохом/отсутствующем экранировании.
'; DROP TABLE users; --'
На этом сайте вы можете попробовать поотачивать свои навыки в XSS 😄
Так же стоит отметить, что принцип таких XSS Атак отдаленно похож на принцип работы такого инструмента как Google Tag Manager. Поэтому, рекомендую вам копнуть в эту тему глубже если планируете развиваться как Веб-аналитик.
https://xss-game.appspot.com/
👍4🔥2
