Я не понимаю какая переменная/атрибут меняется, ведь я хочу получить ошибку AttributeError
from dotenv import load_dotenv
import os
from googleapiclient.discovery import build
import json
load_dotenv()
class Channel:
"""Класс для ютуб-канала"""
youtube = build('youtube', 'v3', developerKey=os.getenv("API_YOU_TUBE"))
def __init__(self, channel_id: str) -> None:
"""Экземпляр инициализируется id канала. Дальше все данные будут подтягиваться по API."""
self.__channel_id = channel_id
self.title = self.get_data_init()["items"][0]["snippet"]["title"]
self.description = self.get_data_init()["items"][0]["snippet"]["description"]
self.url = "https://www.youtube.com/channel/" + self.get_data_init()["items"][0]["id"]
self.subscriber_count = self.get_data_init()["items"][0]["statistics"]["subscriberCount"]
self.video_count = self.get_data_init()["items"][0]["statistics"]["videoCount"]
self.view_count = self.get_data_init()["items"][0]["statistics"]["viewCount"]
def print_info(self) -> None:
"""Выводит в консоль информацию о канале."""
channel = self.youtube.channels().list(id=self.__channel_id, part='snippet,statistics').execute()
print(channel)
def get_data_init(self):
channel = self.youtube.channels().list(id=self.__channel_id, part='snippet,statistics').execute()
return channel
@staticmethod
def get_service():
return Channel.youtube
def to_json(self, data):
with open(data, "w") as f:
f.write(str(self.get_data_init()))
def __repr__(self):
return f"{self.__channel_id}, {self.subscriber_count}"
moscowpython = Channel('UC-OVMPlMA3-YCIeg4z5z23A')
moscowpython.__channel_id = 'Новое название'
print(moscowpython.__channel_id)
print(moscowpython)
# не выдает никаких ошибок, хотя должна AttributeError```
Изменено:
вот так выдает ошибку:
moscowpython = Channel('UC-OVMPlMA3-YCIeg4z5z23A')
print(moscowpython.__channel_id)
moscowpython.__channel_id = 'Новое название'
print(moscowpython.__channel_id)
print(moscowpython)
а вот так нет:
moscowpython = Channel('UC-OVMPlMA3-YCIeg4z5z23A')
# print(moscowpython.__channel_id)
moscowpython.__channel_id = 'Новое название'
print(moscowpython.__channel_id)
print(moscowpython)
Ответы (2 шт):
Ошибка AttributeError
связана с вызовом поля __channel_id
объекта у класса Channel
Неправильно:
moscowpython = Channel('UC-OVMPlMA3-YCIeg4z5z23A') # Создание объекта класса
print(moscowpython.__channel_id) # Вызов несуществующего поля
Правильно:
moscowpython = Channel('UC-OVMPlMA3-YCIeg4z5z23A') # Создание объекта класса
moscowpython.__channel_id = 'Новое название' # Присваивание значения полю
print(moscowpython.__channel_id) # Вызов существующего поля
Давайте сперва упростим пример и уберём всё лишнее:
class Channel:
def __init__(self, channel_id: str) -> None:
self.__channel_id = channel_id
def test(self):
print(self.__channel_id )
moscowpython = Channel('UC-OVMPlMA3-YCIeg4z5z23A')
print(dir(moscowpython))
moscowpython.__channel_id = 'Новое название'
print(moscowpython.__channel_id)
moscowpython.test()
print(dir(moscowpython))
Вывод:
['_Channel__channel_id', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getstate__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'test']
Новое название
UC-OVMPlMA3-YCIeg4z5z23A
['_Channel__channel_id', '__channel_id', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getstate__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'test']
[Program finished]
Функция dir() в Python является встроенной функцией, которая возвращает список всех атрибутов и методов объекта.
Теперь обратите внимание на первый список распечатанных объектов, а конкретно на _Channel__channel_id это и есть ваш приватный атрибут.
Во втором списке распечатанных объектов он так же есть, а следовательно мы его не переопределили, что легко проверяется через метод test.
В нашем примере атрибут __channel_id
был определен как private в классе Channel. Приватные атрибуты в Python обрабатываются с использованием механизма, называемого name mangling, который переименовывает атрибуты, чтобы предотвратить их случайное переопределение или доступ к ним за пределами класса.
Когда вы объявляете атрибут с двойным подчеркиванием, Python автоматически переименовывает его в форме _ИмяКласса__ИмяАтрибута
. В нашем случае, атрибут __channel_id будет переименован в _Channel__channel_id
.
Таким образом, если вы попробуете изменить значение moscowpython.__channel_id
, как в нашем примере, Python не выбрасывает ошибку, потому что он не видит __channel_id
как приватный атрибут. Вместо этого, он просто создает новый атрибут __channel_id
в объекте moscowpython, который будет доступен как обычный атрибут, но это уже не тот атрибут, который был определен внутри класса, потому что наш атрибут уже выглядит так - _Channel__channel_id.
Можно немного докрутить идею, что бы нельзя было создавать атрибуты с __
переопределив один из магических методов класса:
class Channel:
def __init__(self, channel_id: str) -> None:
self.__channel_id = channel_id
def __setattr__(self, name, value):
# Если имя атрибута начинается с двойного подчеркивания, выбрасываем исключение
if name.startswith('__'):
raise AttributeError(f"Создание атрибута '{name}' запрещено")
super().__setattr__(name, value)
moscowpython = Channel('UC-OVMPlMA3-YCIeg4z5z23A')
moscowpython.__channel_id = 'Новое название'
Вывод:
Traceback (most recent call last):
File "/data/user/0/ru.iiec.pydroid3/files/accomp_files/iiec_run/iiec_run.py", line 31, in <module>
start(fakepyfile,mainpyfile)
File "/data/user/0/ru.iiec.pydroid3/files/accomp_files/iiec_run/iiec_run.py", line 30, in start
exec(open(mainpyfile).read(), __main__.__dict__)
File "<string>", line 18, in <module>
File "<string>", line 9, in __setattr__
AttributeError: Создание атрибута '__channel_id' запрещено
[Program finished]
И соответственно, уже так ошибку можно отловить:
try:
moscowpython.__channel_id = 'Новое название'
except AttributeError as e:
print(e)