如何执行功能列表以及如何将数据传递给使用asyncio调用的适当功能

时间:2018-09-26 23:56:35

标签: python asynchronous python-asyncio

我以前使用的是请求,但是此后我开始使用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@hotmail.com', 'email@outlook.com', 'email@gmail.com']

我本质上只是想知道如何将多个功能排队,在这种情况下为query_login和send_friend_request,同时还将正确的数据传递给所述功能,假设我要同时在一个社交媒体应用程序上运行三个帐户,令我感到困惑的是,尽管我倾向于使事情变得过于复杂,但任何帮助将不胜感激。

2 个答案:

答案 0 :(得分:4)

Python旨在通过unpack运算符*或使用lambda使其变得相当容易。该线程中有几个您需要的好答案:

Passing functions with arguments to another function in 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.

另一种方法是只制作一个lambda。

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.

Voila!全部归功于另一个主题中的作者。这里有一个陷阱:如果将对象作为参数传递,则它将作为引用传递。注意,因为您可以在对象被调用之前先对其进行修改。

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))) 

在lambda函数中,整个事物在调用时进行求值,因此即使通常不是引用的事物的行为也类似于引用。上面的技巧不起作用,因此在创建lambda之前,请根据需要进行任何复制。

答案 1 :(得分:0)

好的,这真的很酷,让我向您展示一些东西:

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)来包装协程(协程函数的返回值),从而从非异步代码中调用协程。该协程现在将在后台运行。您可以向将来添加回调,该回调将在完成时调用,并传递一个参数:将来对象本身。 Look up futures in the asyncio documentation(如果您想了解更多有关它们的信息,请查看Coroutines and 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))

,但是做一个更好的方法可能只是使async 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确实很棒,我想向您展示。它有很多用途,我可以保证您将需要使用这两种方法来完成复杂的工作。

很抱歉,墙上的文字混乱无章。我在这里有点新。我只是认为asyncio真的很酷。