LINUX.ORG.RU

Учился делать Telegram-бота

 ,


1

1

Привет, ЛОР!
Есть один...

В общем, пытался в Python и Telegram API, не уверен в правильности кода и/или некоторых решений, хочу, чтобы Python-гуру оценили код и подсказали что и где неплохо бы исправить.

Бот ещё не допилен, но желающие могут его себе, т.к. все известные мне с похожим функционалом — closed-source, а значит скорее всего сливают логи из ваших чатов доброму создателю.

Из возможностей — разного рода рулеточки, статистика (WIP), запись логов и что-то ещё.
Можно легко добавлять свои собственные команды, если хоть немного знакомы с удавом.
А, ну и я осилил интерфейс конфигурирования бота с собстенными конфигами для каждой конфы — этого вообще нет нигде (ибо ограничения Telegram API) и реализовывать пришлось через одно место.

Зачем тред? Всё вышеописанное, да и просто так, чтобы, так скзть, добро не пропадало даром.
Спасибо за внимание.

NB! Если зашли в тред напомнить, что Телеграм не нужен — все и так это знают, не нужно на это тратить байты!

★★★★☆

Последнее исправление: annerleen (всего исправлений: 1)

Ну, например, для меня было открытием, что при декларировании класса:

class TelegramBot:

  var1 = None

  def test(self, val):
    self.var1 = val
...


..и создании его инстансов и работе с ними:

tg1 = TelegramBot()
tg2 = TelegramBot()
tg1.test(222)
tg2.test(111)


свойство tg1.var1 иногда (!) ВНЕЗАПНО принимало значение 1 (и наоборот).
При попытке воспроизвести специально — всё работало как нужно.

Помогло в
__init__()
инициализировать эти переменные ещё раз.


Это баг, фича, или я наркоман, например?

annerleen ★★★★☆
() автор топика
Последнее исправление: annerleen (всего исправлений: 3)
Ответ на: комментарий от annerleen

я постил не так давно с лёгкостью. сейчас проверил - и правда, бан. правда не за то что иностранный IP, а за то что IP датацентра

eternal_sorrow ★★★★★
()
Ответ на: комментарий от eternal_sorrow

Ну, подсети домашней Telia EE забанены, мобильной — можно.

Где логика — не знаю.
После покупки мейлру все подсети, похожие на датацентры, попали в бан, чтобы писать с VPN — нужно купить пасскей, причём с идентифицированного QIWI/YM.
Ну, вы понели.

annerleen ★★★★☆
() автор топика
Последнее исправление: annerleen (всего исправлений: 1)
Ответ на: комментарий от annerleen

покупка мейл ру давно уже была, а забанили буквально на днях. причём даже в /int/ нельзя постить

eternal_sorrow ★★★★★
()
Ответ на: комментарий от annerleen

Насколько я помню ООП в питоне, то область видимости этих переменных такая:

class TelegramBot:

  var1 = None # это классовая переменная, т.е. static

  def test(self, val):
    self.var1 = val # это переменная экземпляра класса
...
bvn13 ★★★★★
()
Ответ на: комментарий от bvn13

То есть, переобъявлять переменные в __init__(self) — это ещё и нормально?
upd: в таком простом примере всё работает как положено.

class SomeClass:
	attr1 = 42
	attr2 = "Hello"


s1 = SomeClass()
s2 = SomeClass()

s1.attr2 = "Some other value"
print(s2.attr2)


но «в полях» при работе с двумя экземплярами класса ВНЕЗАПНО в первый экземпляр могло попасть значение аттрибута второго.

annerleen ★★★★☆
() автор топика
Последнее исправление: annerleen (всего исправлений: 1)
Ответ на: комментарий от annerleen

а, вот, нашёл как это воспроизвести.

class SomeClass(object):
	users = []

	def add(self, s):
		self.users.append(s)


obj1 = SomeClass()
obj2 = SomeClass()

obj1.add("something")
print(obj2.users) # ['something'] -- wtf?


какого чёрта в этом вашем удаве свойства статические по-дефолту — я не понимать.
ладно, окей, упоролись и по-умолчанию аттрибуты static.
какого чёрта *static аттрибут* можно перезаписывать через self, который какбэ указатель на текущий экземпляр?

«// счастливой отладки» (c)

annerleen ★★★★☆
() автор топика
Последнее исправление: annerleen (всего исправлений: 1)
Ответ на: комментарий от bvn13

какого чёрта *static аттрибут* можно перезаписывать через self, который какбэ указатель на текущий экземпляр?

annerleen ★★★★☆
() автор топика
Ответ на: комментарий от annerleen

Кто сказал, что ты ее перезаписываешь?

C:\dev\temp>cat test.py

class Test(object) :
    bar = 'lol'

    def foo(self, s) :
        self.bar = s

obj1 = Test()
obj2 = Test()

obj1.foo('passed')

print(obj1.bar)
print(obj2.bar)
print(Test.bar)

результат

C:\dev\temp>python test.py
passed
lol
lol
bvn13 ★★★★★
()
Ответ на: комментарий от bvn13

но в твоем случае (я проверяю) все печельней…

                           
class Test(object) :       
    bar = []               
                           
    def foo(self, s) :     
        self.bar.append(s) 
                           
obj1 = Test()              
obj2 = Test()              
                           
obj1.foo('passed')         
                           
                           
print(obj1.bar)            
print(obj2.bar)            
print(Test.bar)            
C:\dev\temp>python test.py
['passed']
['passed']
['passed']
bvn13 ★★★★★
()
Ответ на: комментарий от bvn13

в упор не вижу отличий от здесь, кроме того, что у меня список, а у тебя строка.

да, именно это я и имел в виду под «иногда».

annerleen ★★★★☆
() автор топика
Последнее исправление: annerleen (всего исправлений: 1)
Ответ на: комментарий от bvn13

да я вижу уже.

Атрибут может быть объявлен приватным (внутренним) с помощью нижнего подчеркивания перед именем, но настоящего скрытия на самом деле не происходит – все на уровне соглашений.

а чего _, а не __ETO_PROTECTED_ATTRIBUTE_MAMOI_KLYANUS_varname сразу?

annerleen ★★★★☆
() автор топика
Ответ на: комментарий от annerleen

это условное соглашение между программистами. подчеркивание ничего не делает (кмк) физически с переменной

bvn13 ★★★★★
()
Ответ на: комментарий от annerleen

вот еще интересно https://stackoverflow.com/a/69067/2798461

>>> t = Test()
>>> t.i     # "static" variable accessed via instance
3
>>> t.i = 5 # but if we assign to the instance ...
>>> Test.i  # we have not changed the "static" variable
3
>>> t.i     # we have overwritten Test.i on t by creating a new attribute t.i
5
>>> Test.i = 6 # to change the "static" variable we do it by assigning to the class
>>> t.i
5
>>> Test.i
6
>>> u = Test()
>>> u.i
6           # changes to t do not affect new instances of Test

# Namespaces are one honking great idea -- let's do more of those!
>>> Test.__dict__
{'i': 6, ...}
>>> t.__dict__
{'i': 5}
>>> u.__dict__
{}
bvn13 ★★★★★
()
Последнее исправление: bvn13 (всего исправлений: 1)
Ответ на: комментарий от annerleen

Есть атрибуты класса. есть атрибуты инстанса, которые через self. :). К тому же есть мутейбл и не мутейбл типы. Списки мутейбл. Так что классы тут не причем

maxim2
()
Ответ на: комментарий от annerleen

users принадлежит классу, всё нормально. Оно шарится между всеми инстансами. У класса и у инстанса свои наборы атрибутов, чтобы атрибут риналлежал инстансу, нужно присвоить его в __init__. Атрибуты инстанса хранятся в .__dict__.

WitcherGeralt ★★
()
Ответ на: комментарий от bvn13

С ООП в питоне полный порядок. Даже лучше чем где бы то ни были из мейнстримных языков. Просто у тебя сознание поражено джавой.

WitcherGeralt ★★
()
Ответ на: комментарий от annerleen

Это не «иногда». В одном случае ты пополняешь список, а во втором перекрываешь переменную. Всё абсолютно логично. Попробуй почитать документацию.

WitcherGeralt ★★
()
Ответ на: комментарий от bvn13
class Foo:
    def __init__(self):
        self._protected = 'protected'
        self.__private = 'Foo private'


class Bar(Foo):
    def __init__(self):
        super().__init__()
        self.__private = 'Bar private'

    def print(self):
        print(self._protected)
        print(self.__private)
        print(self._Foo__private)
Bar().print()
protected
Bar private
Foo private
KillTheCat ★★★★★
()
Ответ на: комментарий от bvn13
>>> class C:
...   __private = 1
...   def test(self):
...     print(self.__private)
...
>>> c = C()
>>> c.test()
1
>>> c.__private
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'C' object has no attribute '__private'
>>> c._C__private
1
WitcherGeralt ★★
()
Ответ на: комментарий от annerleen

какого чёрта static аттрибут можно перезаписывать через self, который какбэ указатель на текущий экземпляр?

Когда ты делаешь self.attr2 = val, ты создаешь новый атрибут в экземпляре, то что он есть в объекте класса вообще не важно.

Когда ты делаешь self.users.append(val) ты получаешь ссылку на объект cls.users т.к. self.users не существует и вызываешь метод этого объекта. list.append не возвращает копию объекта а модифицирует его. Такая же фигня с мутабельными значениями по умолчанию :)

И почему он static аттрибут? В питоне класс это тоже объект:

class Object:
    var1 = 1

    @classmethod
    def set(cls, val):
        cls.var1 = val

    @classmethod
    def get(cls):
        return cls.var1


print(Object.get())
Object.set(5)
print(Object.get())
Object.var1 = 'lol'
print(Object.get())
KillTheCat ★★★★★
()
Ответ на: комментарий от bvn13

Пруф именно переименования:

>>> C.f = lambda self: self.__private
>>> c.f()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 1, in <lambda>
AttributeError: 'C' object has no attribute '__private'
WitcherGeralt ★★
()

Писать дичь, вместо того чтобы читать учебник питона про ООП, классика лора. Отбитые пытаются делать хуяк-хуяк, вместо того чтобы нормально освоить инструмент :(

annerleen читай букварь питона.

fsb4000 ★★★★★
()
Последнее исправление: fsb4000 (всего исправлений: 1)
Ответ на: комментарий от annerleen

https://docs.python.org/3/tutorial/index.html

Серьезно, чтобы создавать тред с вопросом, который разобран в чертовом официальном туториале, надо быть либо отбитым, либо троллем, либо отбитым троллем.

t184256 ★★★★★
()
Последнее исправление: t184256 (всего исправлений: 1)

Таки посмотрел бота. Быдлокод отборнейший с первой до последней строки. Начни хотя бы с pep8 и учебника по Python 2.6 на wikibooks от Гвидо.

Безотносительно самого кода, проект станет радикально лучше, если ты напишешь нормальное ридми и API key вынесешь куда-то в конфиг, на крайняк переменную окружения, либо будешь принимать из stdin, whatever. И, конечно же, нужно описать зависимости в requirements.txt и запилить Dockerfile, чтобы не было совсем уж стыдно это показывать.

WitcherGeralt ★★
()

Отличный проект. И название хорошее.

turtle_bazon ★★★★★
()
Ответ на: комментарий от WitcherGeralt

По основам питона? Нет, потому что я им эти знания должен был выдать и выдавал.

Каких-нибудь второкурсников магистратуры? Видимо «да, еще как».

А что, мои студни засирают форум вопросами, которые разобраны в туториале питона??

t184256 ★★★★★
()

желающие могут его себе

Нет, не могут. Лицензия отсутствует.

с похожим функционалом

Функционал — математическое понятие, возникшее в вариационном исчислении для обозначения переменной величины, заданной на множестве функций, то есть зависящей от выбора одной или нескольких функций.

Зачем ты употребляешь термин в контексте, к нему не относящемся?

пытался в Python и Telegram API

Надо пытаться ещё.

А где ссылка на работающего бота?

vvn_black ★★★★★
()
Ответ на: комментарий от vvn_black

Нет, не могут. Лицензия отсутствует.

нужно срочно добавить WTFPL

Функционал — математическое понятие, возникшее в вариационном исчислении для обозначения переменной величины, заданной на множестве функций, то есть зависящей от выбора одной или нескольких функций.

простите за мой русский, забываю.
исправьте на более корректный вариант, пожалуйста.

А где ссылка на работающего бота?

а в чём смысл, если не локально у себя его пускать?

annerleen ★★★★☆
() автор топика
Ответ на: комментарий от WitcherGeralt

Я не могу так учиться, мне нужны примеры «как не нужно» vs «как нужно», желательно с аргументацией.

Спойлер: в PEP8 её нету.

annerleen ★★★★☆
() автор топика
Ответ на: комментарий от WitcherGeralt

Даже лучше чем где бы то ни были из мейнстримных языков.

Даже в Ruby завезли нормальную инкапсуляцию, понятные модификаторы (или декораторы? как их правильно? $, @, @@, вот это вот всё) и сахарку в придачу (attr_*)

Метатаблицы в Lua уже не кажутся такими ужасными после Удава.

annerleen ★★★★☆
() автор топика
Ответ на: комментарий от WitcherGeralt

Таки посмотрел бота. Быдлокод отборнейший с первой до последней строки

очень попрошу хотя бы несколько примеров «как не надо», заменённое на «как надо».

если ты напишешь нормальное ридми и API key вынесешь куда-то в конфиг, на крайняк переменную окружения, либо будешь принимать из stdin, whatever. И, конечно же, нужно описать зависимости в requirements.txt

Это всё само собой.

и запилить Dockerfile,

чтобы все покрылись фейспалмами от того, что тупого бота запихали аж в контейнер.

annerleen ★★★★☆
() автор топика
Ответ на: комментарий от annerleen

Это соглашение по стилю, ты просто берёшь его и применяешь. Никто тебя уговаривать как детсадовца не будет, ни с аргументацией, ни без. Не хочешь юзать, так добро пожаловать в стан ссаных быдлокодеров.

WitcherGeralt ★★
()
Ответ на: комментарий от annerleen

Руби всё. Никто адекватный в 2019 что-то новое на ней пилить не начнёт.

То, что она более сахарная и гибкая, я не отрицаю, просто нинужно.

WitcherGeralt ★★
()
Ответ на: комментарий от annerleen

Ачотакова? Если кто-то решит его запустить, то это самый лучший способ.

Я тоже такую херню писал почти 10 лет назад. Только для джаббера. «Bydlo-bot» называлось, он начислял очки за ругательства и выдавал за них привилегии в конференции, плюс сам эпично матерился и рандомно выгонял тех, кто вёл себя тихо.

WitcherGeralt ★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.