Я ранее пользовался запросами, но с тех пор я перешел на aiohttp + asyncio, чтобы запускать учетные записи параллельно, однако мне не удается объединить логику в моей голове.
class Faked(object):
def __init__(self):
self.database = sqlite3.connect('credentials.db')
async def query_login(self, email):
print(email)
cur = self.database.cursor()
sql_q = """SELECT * from user WHERE email='{0}'""".format(email)
users = cur.execute(sql_q)
row = users.fetchone()
if row is None:
raise errors.ToineyError('No user was found with email: ' + email + ' in database!')
self.logger().debug("Logging into account '{0}'!".format(row[0]))
call_func = await self._api.login(data={'email': row[0],
'password': row[1],
'deviceId': row[2],
'aaid': row[3]})
return await call_func
async def send_friend_request(self, uid):
return await self._api.send_friend_request(uid)
def main(funcs, data=None):
"""
todo: fill
:rtype: object
"""
tasks = []
if isinstance(funcs, list):
for func in funcs:
tasks.append(func)
else:
tasks.append(funcs)
print(tasks)
loop = asyncio.get_event_loop()
results = loop.run_until_complete(asyncio.gather(*tasks))
for result in results:
print(result)
return results
if __name__ == '__main__': # for testing purposes mostly
emails = ['[email protected]', '[email protected]', '[email protected]']
Я просто хочу знать, как ставить в очередь несколько функций, в этом случае query_login и send_friend_request, а также передавать правильные данные указанным функциям, предполагая, что я буду запускать три аккаунта, скажем, приложение для социальных сетей одновременно, это действительно пугает мой разум, хотя у меня есть склонность к чрезмерным последствиям, любая помощь будет очень признательна.
Python предназначен для упрощения работы с оператором распаковки * или с помощью лямбда. В этой теме есть несколько хороших ответов, которые имеют то, что вам нужно:
Передача функций с аргументами другой функции в Python?
Позвольте пройти через него.
callstack = [] # initialize a list to serve as our stack.
# See also collections.deque for a queue.
Тогда мы можем определить нашу функцию:
def somefunc(a, b, c):
do stuff...
Затем добавьте вызов в стек с аргументами в виде списка.
args = [a, b, c]
callstack.append((somefunc, args)) # append a tuple with the function
# and its arguments list.
# calls the next item in the callstack
def call_next(callstack):
func, args = callstack.pop() # unpack our tuple
func(*args) # calls the func with the args unpacked
Оператор * распаковывает ваш список и предоставляет их в качестве аргументов по порядку. Вы также можете распаковать аргументы ключевого слова оператором двойной звезды (**).
def call_next(callstack):
func, args, kwargs = callstack.pop() # unpack our tuple
func(*args, **kwargs) # calls the func with both args and kwargs unpacked.
Альтернативный способ - просто создать лямбду.
def add(a, b):
return a + b
callstack = []
callstack.append(lambda: add(1, 2))
callstack.pop()() # pops the lambda function, then calls the lambda function,
# which just calls the function as you specified it.
Вуаля! Все они принадлежат авторам в другой теме. Здесь есть информация: если вы передаете объект в качестве аргумента, он будет передан как ссылка. Будьте осторожны, потому что вы можете изменить объект, прежде чем он будет вызван в вашем стеке.
def add(a, b, c):
return a + b + c
badlist = [1,2,3]
callstack.append((somefunc, badlist))
badlist = [2, 4, 6]
callstack.append((somefunc, badlist))
while len(callstack) > 0:
print(call_next(callstack))
# Prints:
12
12
Вы можете обойти это в версии * args с:
# make a shallow copy and pass that to the stack instead.
callstack.append((somefunc, list(badlist)))
В лямбда-функциях вся вещь оценивается во время вызова, поэтому даже те вещи, которые обычно не относятся к ссылке, ведут себя как ссылки. Вышеприведенный трюк не будет работать, поэтому сделайте любое копирование по мере необходимости, прежде чем создавать свою лямбду.
Хорошо, так что это действительно круто, позвольте мне показать вам кое-что:
loop = asyncio.get_event_loop()
api = MyAPIToSomeCoolChatProgram()
def my_done_callback(fut):
exc = fut.exception()
if exc:
print(fut.my_custom_attribute, 'raised an exception!')
import traceback
traceback.print_exc(exc) # dumps a "Traceback (most recent call last):" message to stderr
print(fut.my_custom_attribute, 'completed, returned', repr(fut.result()))
fut1 = asyncio.ensure_future(api.send_friend_request(my_best_friend))
fut1.my_custom_attribute = 'fut1 (add a friend)'
fut1.add_done_callback(my_done_callback)
fut2 = asyncio.ensure_future(api.post_text_message('Hello everybody!'))
fut2.my_custom_attribute = 'fut2 (send a message)'
fut2.add_done_callback(my_done_callback)
print('Done creating the futures')
loop.run_forever()
Выход:
Done creating the futures
fut1 (add a friend request) completed, returned '200 OK'
fut2 (send a message) completed, returned '200 OK'
Обратите внимание, что они могут отображаться в любом порядке. Вы можете вызывать сопрограммы из неасинхронного кода путем переноса сопрограммы (возвращаемого значения из функции сопрограммы) в будущем (или, точнее, в Task
которая является подклассом Future
). Эта сопрограмма теперь будет работать в фоновом режиме. Вы можете добавить обратный вызов в будущее, которое будет вызываться, когда оно заканчивается, передает один аргумент: сам будущий объект. Посмотрите фьючерсы в документации по асинхронному обложению, если вы хотите узнать о них больше (также проверьте Coroutines и Tasks).
В любом случае, эти обратные вызовы могут делать все, что вы хотите, включая запуск других задач.
def when_done_logging_in(self, fut):
self.login_info = fut.result() # note: calling fut.result() if the login coroutine raised an exception will reraise the exception here.
next_fut = asyncio.ensure_future(self.send_friend_request(fut.friend_request_to_send))
# do something with next_fut here (or don't if you don't care about the result)
def login_and_send_friend_request(self, email, friend):
fut = asyncio.ensure_future(self.query_login(email))
fut.friend_request_to_send = friend
fut.add_done_callback(self.when_done_logging_in)
Конечно, вы также можете сделать это с помощью:
async def login_and_send_friend_request(self, email, friend):
self.login_info = await self.query_login(email)
await self.send_friend_request(friend)
что было бы лучше, потому что все исключения фактически обрабатывались надлежащим образом, а не просто игнорировались. Вы также можете это сделать, если вы заранее знаете электронное письмо (которое вы не можете):
def __init__(self, whatever_args_you_might_have_here, email):
...
self.login_info = None
self.email = email
async def send_friend_request(self, uid):
if self.login_info is None:
await self.query_login(self.email) # if you end up doing this you should probably make this not take a parameter and just use self.email instead
do_send_friend_request_stuff()
Конечно, вы можете не знать адрес электронной почты до тех пор, пока объект не будет создан, и в этом случае вы можете либо инициализировать его до "Нет" до тех пор, пока не будет вызвана какая-либо функция входа в систему, либо используйте один из первых двух способов.
Если вы хотите последовательно выполнять список функций, вы можете сделать следующее:
def execute_coros_in_sequence(list_of_coros):
fut=asyncio.ensure_future(list_of_coros[0])
if len(list_of_coros) > 1:
# there is probably a better way to do this
fut.remaining_coros=list_of_coros[1:]
fut.add_done_callback(lambda fut: execute_coros_in_sequence(fut.remaining_coros))
но, вероятно, лучшим способом сделать это было бы просто заставить функцию aync def вызывать их всех, потому что таким образом вы получаете обработку исключений и т.д. без большого избыточного усложнения. Лучший способ сделать это, если вы хотите, чтобы это как будущее (которое вы также можете сохранить как атрибут объекта и запрос, чтобы увидеть, если оно еще сделано), будет следующим:
class API:
async def login(self):
pass
async def logout(self):
pass
async def do_fun_stuff(self):
pass
async def test_api(api):
api.login()
api.do_fun_stuff()
api.logout()
fut=asyncio.create_task(test_part_of_api(API()))
(Кстати, asyncio.ensure_future()
сначала проверяет, является ли его аргумент уже будущим, а если нет, вызывает asyncio.create_task()
.)
но будущее api действительно круто, и я хотел показать его вам. Для этого есть использование, и я могу в значительной степени гарантировать, что вам понадобятся оба этих подхода, чтобы сделать что-то сложное.
Извините за стену текста неорганизованного ответа. Я здесь немного новый. Я просто думаю, что асинчио действительно круто.