标签归档:function

是否有一种简单的方法来腌制python函数(或以其他方式序列化其代码)?

问题:是否有一种简单的方法来腌制python函数(或以其他方式序列化其代码)?

我正在尝试通过网络连接(使用asyncore)传输功能。是否有一种简单的方法来序列化python函数(至少在这种情况下不会有副作用),以便像这样进行传输?

理想情况下,我希望有一对类似于以下的函数:

def transmit(func):
    obj = pickle.dumps(func)
    [send obj across the network]

def receive():
    [receive obj from the network]
    func = pickle.loads(s)
    func()

I’m trying to transfer a function across a network connection (using asyncore). Is there an easy way to serialize a python function (one that, in this case at least, will have no side effects) for transfer like this?

I would ideally like to have a pair of functions similar to these:

def transmit(func):
    obj = pickle.dumps(func)
    [send obj across the network]

def receive():
    [receive obj from the network]
    func = pickle.loads(s)
    func()

回答 0

您可以序列化函数字节码,然后在调用方上对其进行重构。所述编组模块可以用于串行化处理的代码对象,然后可将其重新组装成一个函数。即:

import marshal
def foo(x): return x*x
code_string = marshal.dumps(foo.func_code)

然后在远程过程中(在传输code_string之后):

import marshal, types

code = marshal.loads(code_string)
func = types.FunctionType(code, globals(), "some_func_name")

func(10)  # gives 100

一些警告:

  • 元帅的格式(与此有关的任何python字节码)在主要python版本之间可能不兼容。

  • 仅适用于cpython实现。

  • 如果该函数引用了您需要使用的全局变量(包括导入的模块,其他函数等),则也需要对它们进行序列化或在远程端重新创建它们。我的示例只是为它提供了远程进程的全局命名空间。

  • 您可能需要做更多的工作来支持更复杂的情况,例如闭包或生成器函数。

You could serialise the function bytecode and then reconstruct it on the caller. The marshal module can be used to serialise code objects, which can then be reassembled into a function. ie:

import marshal
def foo(x): return x*x
code_string = marshal.dumps(foo.func_code)

Then in the remote process (after transferring code_string):

import marshal, types

code = marshal.loads(code_string)
func = types.FunctionType(code, globals(), "some_func_name")

func(10)  # gives 100

A few caveats:

  • marshal’s format (any python bytecode for that matter) may not be compatable between major python versions.

  • Will only work for cpython implementation.

  • If the function references globals (including imported modules, other functions etc) that you need to pick up, you’ll need to serialise these too, or recreate them on the remote side. My example just gives it the remote process’s global namespace.

  • You’ll probably need to do a bit more to support more complex cases, like closures or generator functions.


回答 1

请查看Dill,它扩展了Python的pickle库以支持更多类型,包括函数:

>>> import dill as pickle
>>> def f(x): return x + 1
...
>>> g = pickle.dumps(f)
>>> f(1)
2
>>> pickle.loads(g)(1)
2

它还支持对函数闭包中对象的引用:

>>> def plusTwo(x): return f(f(x))
...
>>> pickle.loads(pickle.dumps(plusTwo))(1)
3

Check out Dill, which extends Python’s pickle library to support a greater variety of types, including functions:

>>> import dill as pickle
>>> def f(x): return x + 1
...
>>> g = pickle.dumps(f)
>>> f(1)
2
>>> pickle.loads(g)(1)
2

It also supports references to objects in the function’s closure:

>>> def plusTwo(x): return f(f(x))
...
>>> pickle.loads(pickle.dumps(plusTwo))(1)
3

回答 2


回答 3

最简单的方法可能是inspect.getsource(object)(请参阅inspect模块),该方法返回带有函数或方法的源代码的String。

The most simple way is probably inspect.getsource(object) (see the inspect module) which returns a String with the source code for a function or a method.


回答 4

这完全取决于您是否在运行时生成函数:

如果这样做- inspect.getsource(object)因为动态生成的函数会从.py文件中获取对象的源代码,因此不适用于动态生成的函数,因此只能将在执行之前定义的函数作为源来检索。

而且,如果您的函数仍然放置在文件中,为什么不让接收者访问它们,而只传递模块和函数名。

我能想到的动态创建函数的唯一解决方案是在发送,发送源然后eval()在接收器端将其构造为字符串。

编辑:该marshal解决方案看起来也很聪明,不知道您可以序列化其他内置的东西

It all depends on whether you generate the function at runtime or not:

If you do – inspect.getsource(object) won’t work for dynamically generated functions as it gets object’s source from .py file, so only functions defined before execution can be retrieved as source.

And if your functions are placed in files anyway, why not give receiver access to them and only pass around module and function names.

The only solution for dynamically created functions that I can think of is to construct function as a string before transmission, transmit source, and then eval() it on the receiver side.

Edit: the marshal solution looks also pretty smart, didn’t know you can serialize something other thatn built-ins


回答 5

cloud封装(PIP安装云)可以泡制任意代码,包括依赖关系。见https://stackoverflow.com/a/16891169/1264797

The cloud package (pip install cloud) can pickle arbitrary code, including dependencies. See https://stackoverflow.com/a/16891169/1264797.


回答 6

code_string ='''
def foo(x):
    返回x * 2
def bar(x):
    返回x ** 2
'''

obj = pickle.dumps(code_string)

现在

exec(pickle.loads(obj))

富(1)
> 2
酒吧(3)
> 9
code_string = '''
def foo(x):
    return x * 2
def bar(x):
    return x ** 2
'''

obj = pickle.dumps(code_string)

Now

exec(pickle.loads(obj))

foo(1)
> 2
bar(3)
> 9

回答 7

你可以这样做:

def fn_generator():
    def fn(x, y):
        return x + y
    return fn

现在,transmit(fn_generator())将发送实际定义fn(x,y)而不是对模块名称的引用。

您可以使用相同的技巧通过网络发送类。

You can do this:

def fn_generator():
    def fn(x, y):
        return x + y
    return fn

Now, transmit(fn_generator()) will send the actual definiton of fn(x,y) instead of a reference to the module name.

You can use the same trick to send classes across network.


回答 8

该模块使用的基本功能涵盖了您的查询,此外,您还可以通过网络获得最佳的压缩效果;参见说明性源代码:

y_serial.py模块::使用SQLite仓库Python对象

“序列化+持久性::在几行代码中,将Python对象压缩并注释为SQLite;然后稍后按关键字顺​​序按顺序检索它们,而无需任何SQL。数据库最有用的”标准”模块用于存储较少模式的数据。”

http://yserial.sourceforge.net

The basic functions used for this module covers your query, plus you get the best compression over the wire; see the instructive source code:

y_serial.py module :: warehouse Python objects with SQLite

“Serialization + persistance :: in a few lines of code, compress and annotate Python objects into SQLite; then later retrieve them chronologically by keywords without any SQL. Most useful “standard” module for a database to store schema-less data.”

http://yserial.sourceforge.net


回答 9

Cloudpickle可能就是您想要的。Cloudpickle描述如下:

cloudpickle对于群集计算特别有用,在群集计算中,Python代码通过网络传送以在可能接近数据的远程主机上执行。

用法示例:

def add_one(n):
  return n + 1

pickled_function = cloudpickle.dumps(add_one)
pickle.loads(pickled_function)(42)

Cloudpickle is probably what you are looking for. Cloudpickle is described as follows:

cloudpickle is especially useful for cluster computing where Python code is shipped over the network to execute on remote hosts, possibly close to the data.

Usage example:

def add_one(n):
  return n + 1

pickled_function = cloudpickle.dumps(add_one)
pickle.loads(pickled_function)(42)

回答 10

这是一个帮助程序类,您可以用来包装函数以使它们可腌制。已经提到的注意事项marshal将适用,但是将尽一切可能使用泡菜。不会在序列化过程中保留全局或闭包。

    class PicklableFunction:
        def __init__(self, fun):
            self._fun = fun

        def __call__(self, *args, **kwargs):
            return self._fun(*args, **kwargs)

        def __getstate__(self):
            try:
                return pickle.dumps(self._fun)
            except Exception:
                return marshal.dumps((self._fun.__code__, self._fun.__name__))

        def __setstate__(self, state):
            try:
                self._fun = pickle.loads(state)
            except Exception:
                code, name = marshal.loads(state)
                self._fun = types.FunctionType(code, {}, name)

Here is a helper class you can use to wrap functions in order to make them picklable. Caveats already mentioned for marshal will apply but an effort is made to use pickle whenever possible. No effort is made to preserve globals or closures across serialization.

    class PicklableFunction:
        def __init__(self, fun):
            self._fun = fun

        def __call__(self, *args, **kwargs):
            return self._fun(*args, **kwargs)

        def __getstate__(self):
            try:
                return pickle.dumps(self._fun)
            except Exception:
                return marshal.dumps((self._fun.__code__, self._fun.__name__))

        def __setstate__(self, state):
            try:
                self._fun = pickle.loads(state)
            except Exception:
                code, name = marshal.loads(state)
                self._fun = types.FunctionType(code, {}, name)

程序退出前做些事情

问题:程序退出前做些事情

在程序退出之前,如何拥有要执行的功能或某些东西?我有一个脚本会在后台不断运行,并且我需要它在退出之前将一些数据保存到文件中。有这样做的标准方法吗?

How can you have a function or something that will be executed before your program quits? I have a script that will be constantly running in the background, and I need it to save some data to a file before it exits. Is there a standard way of doing this?


回答 0

检出atexit模块:

http://docs.python.org/library/atexit.html

例如,如果我想在应用程序终止时打印一条消息:

import atexit

def exit_handler():
    print 'My application is ending!'

atexit.register(exit_handler)

请注意,这对于正常终止脚本非常有用,但是在所有情况下都不会调用它(例如致命的内部错误)。

Check out the atexit module:

http://docs.python.org/library/atexit.html

For example, if I wanted to print a message when my application was terminating:

import atexit

def exit_handler():
    print 'My application is ending!'

atexit.register(exit_handler)

Just be aware that this works great for normal termination of the script, but it won’t get called in all cases (e.g. fatal internal errors).


回答 1

如果您希望某些东西始终运行,即使出现错误,也可以try: finally:这样使用-

def main():
    try:
        execute_app()
    finally:
        handle_cleanup()

if __name__=='__main__':
    main()

如果您还想处理异常,可以在except:之前插入finally:

If you want something to always run, even on errors, use try: finally: like this –

def main():
    try:
        execute_app()
    finally:
        handle_cleanup()

if __name__=='__main__':
    main()

If you want to also handle exceptions you can insert an except: before the finally:


回答 2

如果通过引发KeyboardInterrupt(例如,按Ctrl-C)停止脚本,则可以将其作为标准异常捕获。您也可以SystemExit用相同的方式捕捉。

try:
    ...
except KeyboardInterrupt:
    # clean up
    raise

我提到这一点只是为了让您知道。做到这一点的“正确”方法是上述atexit模块。

If you stop the script by raising a KeyboardInterrupt (e.g. by pressing Ctrl-C), you can catch that just as a standard exception. You can also catch SystemExit in the same way.

try:
    ...
except KeyboardInterrupt:
    # clean up
    raise

I mention this just so that you know about it; the ‘right’ way to do this is the atexit module mentioned above.


如何使用Python发送电子邮件?

问题:如何使用Python发送电子邮件?

这段代码有效,并向我发送了一封电子邮件:

import smtplib
#SERVER = "localhost"

FROM = 'monty@python.com'

TO = ["jon@mycompany.com"] # must be a list

SUBJECT = "Hello!"

TEXT = "This message was sent with Python's smtplib."

# Prepare actual message

message = """\
From: %s
To: %s
Subject: %s

%s
""" % (FROM, ", ".join(TO), SUBJECT, TEXT)

# Send the mail

server = smtplib.SMTP('myserver')
server.sendmail(FROM, TO, message)
server.quit()

但是,如果我尝试将其包装在这样的函数中:

def sendMail(FROM,TO,SUBJECT,TEXT,SERVER):
    import smtplib
    """this is some test documentation in the function"""
    message = """\
        From: %s
        To: %s
        Subject: %s
        %s
        """ % (FROM, ", ".join(TO), SUBJECT, TEXT)
    # Send the mail
    server = smtplib.SMTP(SERVER)
    server.sendmail(FROM, TO, message)
    server.quit()

并称其为以下错误:

 Traceback (most recent call last):
  File "C:/Python31/mailtest1.py", line 8, in <module>
    sendmail.sendMail(sender,recipients,subject,body,server)
  File "C:/Python31\sendmail.py", line 13, in sendMail
    server.sendmail(FROM, TO, message)
  File "C:\Python31\lib\smtplib.py", line 720, in sendmail
    self.rset()
  File "C:\Python31\lib\smtplib.py", line 444, in rset
    return self.docmd("rset")
  File "C:\Python31\lib\smtplib.py", line 368, in docmd
    return self.getreply()
  File "C:\Python31\lib\smtplib.py", line 345, in getreply
    raise SMTPServerDisconnected("Connection unexpectedly closed")
smtplib.SMTPServerDisconnected: Connection unexpectedly closed

谁能帮我理解为什么?

This code works and sends me an email just fine:

import smtplib
#SERVER = "localhost"

FROM = 'monty@python.com'

TO = ["jon@mycompany.com"] # must be a list

SUBJECT = "Hello!"

TEXT = "This message was sent with Python's smtplib."

# Prepare actual message

message = """\
From: %s
To: %s
Subject: %s

%s
""" % (FROM, ", ".join(TO), SUBJECT, TEXT)

# Send the mail

server = smtplib.SMTP('myserver')
server.sendmail(FROM, TO, message)
server.quit()

However if I try to wrap it in a function like this:

def sendMail(FROM,TO,SUBJECT,TEXT,SERVER):
    import smtplib
    """this is some test documentation in the function"""
    message = """\
        From: %s
        To: %s
        Subject: %s
        %s
        """ % (FROM, ", ".join(TO), SUBJECT, TEXT)
    # Send the mail
    server = smtplib.SMTP(SERVER)
    server.sendmail(FROM, TO, message)
    server.quit()

and call it I get the following errors:

 Traceback (most recent call last):
  File "C:/Python31/mailtest1.py", line 8, in <module>
    sendmail.sendMail(sender,recipients,subject,body,server)
  File "C:/Python31\sendmail.py", line 13, in sendMail
    server.sendmail(FROM, TO, message)
  File "C:\Python31\lib\smtplib.py", line 720, in sendmail
    self.rset()
  File "C:\Python31\lib\smtplib.py", line 444, in rset
    return self.docmd("rset")
  File "C:\Python31\lib\smtplib.py", line 368, in docmd
    return self.getreply()
  File "C:\Python31\lib\smtplib.py", line 345, in getreply
    raise SMTPServerDisconnected("Connection unexpectedly closed")
smtplib.SMTPServerDisconnected: Connection unexpectedly closed

Can anyone help me understand why?


回答 0

我建议您使用标准软件包emailsmtplib一起发送电子邮件。请查看以下示例(从Python文档复制)。请注意,如果采用这种方法,“简单”任务确实很简单,而更复杂的任务(如附加二进制对象或发送纯文本/ HTML多部分消息)则可以很快完成。

# Import smtplib for the actual sending function
import smtplib

# Import the email modules we'll need
from email.mime.text import MIMEText

# Open a plain text file for reading.  For this example, assume that
# the text file contains only ASCII characters.
with open(textfile, 'rb') as fp:
    # Create a text/plain message
    msg = MIMEText(fp.read())

# me == the sender's email address
# you == the recipient's email address
msg['Subject'] = 'The contents of %s' % textfile
msg['From'] = me
msg['To'] = you

# Send the message via our own SMTP server, but don't include the
# envelope header.
s = smtplib.SMTP('localhost')
s.sendmail(me, [you], msg.as_string())
s.quit()

要将电子邮件发送到多个目的地,您还可以按照Python文档中的示例进行操作:

# Import smtplib for the actual sending function
import smtplib

# Here are the email package modules we'll need
from email.mime.image import MIMEImage
from email.mime.multipart import MIMEMultipart

# Create the container (outer) email message.
msg = MIMEMultipart()
msg['Subject'] = 'Our family reunion'
# me == the sender's email address
# family = the list of all recipients' email addresses
msg['From'] = me
msg['To'] = ', '.join(family)
msg.preamble = 'Our family reunion'

# Assume we know that the image files are all in PNG format
for file in pngfiles:
    # Open the files in binary mode.  Let the MIMEImage class automatically
    # guess the specific image type.
    with open(file, 'rb') as fp:
        img = MIMEImage(fp.read())
    msg.attach(img)

# Send the email via our own SMTP server.
s = smtplib.SMTP('localhost')
s.sendmail(me, family, msg.as_string())
s.quit()

正如你所看到的,头部ToMIMEText对象必须是由用逗号分隔的电子邮件地址的字符串。另一方面,该sendmail函数的第二个参数必须是字符串列表(每个字符串是一个电子邮件地址)。

因此,如果您有三个电子邮件地址:person1@example.comperson2@example.comperson3@example.com,则可以执行以下操作(省略了明显的部分):

to = ["person1@example.com", "person2@example.com", "person3@example.com"]
msg['To'] = ",".join(to)
s.sendmail(me, to, msg.as_string())

",".join(to)部分使列表中的单个字符串以逗号分隔。

从您的问题中我收集到,您还没有阅读过Python教程 -如果您想在Python上学到任何东西,这是必须的-对于标准库来说,该文档非常有用。

I recommend that you use the standard packages email and smtplib together to send email. Please look at the following example (reproduced from the Python documentation). Notice that if you follow this approach, the “simple” task is indeed simple, and the more complex tasks (like attaching binary objects or sending plain/HTML multipart messages) are accomplished very rapidly.

# Import smtplib for the actual sending function
import smtplib

# Import the email modules we'll need
from email.mime.text import MIMEText

# Open a plain text file for reading.  For this example, assume that
# the text file contains only ASCII characters.
with open(textfile, 'rb') as fp:
    # Create a text/plain message
    msg = MIMEText(fp.read())

# me == the sender's email address
# you == the recipient's email address
msg['Subject'] = 'The contents of %s' % textfile
msg['From'] = me
msg['To'] = you

# Send the message via our own SMTP server, but don't include the
# envelope header.
s = smtplib.SMTP('localhost')
s.sendmail(me, [you], msg.as_string())
s.quit()

For sending email to multiple destinations, you can also follow the example in the Python documentation:

# Import smtplib for the actual sending function
import smtplib

# Here are the email package modules we'll need
from email.mime.image import MIMEImage
from email.mime.multipart import MIMEMultipart

# Create the container (outer) email message.
msg = MIMEMultipart()
msg['Subject'] = 'Our family reunion'
# me == the sender's email address
# family = the list of all recipients' email addresses
msg['From'] = me
msg['To'] = ', '.join(family)
msg.preamble = 'Our family reunion'

# Assume we know that the image files are all in PNG format
for file in pngfiles:
    # Open the files in binary mode.  Let the MIMEImage class automatically
    # guess the specific image type.
    with open(file, 'rb') as fp:
        img = MIMEImage(fp.read())
    msg.attach(img)

# Send the email via our own SMTP server.
s = smtplib.SMTP('localhost')
s.sendmail(me, family, msg.as_string())
s.quit()

As you can see, the header To in the MIMEText object must be a string consisting of email addresses separated by commas. On the other hand, the second argument to the sendmail function must be a list of strings (each string is an email address).

So, if you have three email addresses: person1@example.com, person2@example.com, and person3@example.com, you can do as follows (obvious sections omitted):

to = ["person1@example.com", "person2@example.com", "person3@example.com"]
msg['To'] = ",".join(to)
s.sendmail(me, to, msg.as_string())

the ",".join(to) part makes a single string out of the list, separated by commas.

From your questions I gather that you have not gone through the Python tutorial – it is a MUST if you want to get anywhere in Python – the documentation is mostly excellent for the standard library.


回答 1

好吧,您想要一个最新的答案。

这是我的答案:

当我需要使用python进行邮件发送时,我会使用mailgun API,这让发送邮件整理得很头疼。他们有一个出色的app / api,可让您每月免费发送10,000封电子邮件。

发送电子邮件是这样的:

def send_simple_message():
    return requests.post(
        "https://api.mailgun.net/v3/YOUR_DOMAIN_NAME/messages",
        auth=("api", "YOUR_API_KEY"),
        data={"from": "Excited User <mailgun@YOUR_DOMAIN_NAME>",
              "to": ["bar@example.com", "YOU@YOUR_DOMAIN_NAME"],
              "subject": "Hello",
              "text": "Testing some Mailgun awesomness!"})

您还可以跟踪事件等,请参阅快速入门指南

希望这个对你有帮助!

Well, you want to have an answer that is up-to-date and modern.

Here is my answer:

When I need to mail in Python, I use the mailgun API wich get’s a lot of the headaches with sending mails sorted out. They have a wonderfull app/api that allows you to send 5,000 free emails per month.

Sending an email would be like this:

def send_simple_message():
    return requests.post(
        "https://api.mailgun.net/v3/YOUR_DOMAIN_NAME/messages",
        auth=("api", "YOUR_API_KEY"),
        data={"from": "Excited User <mailgun@YOUR_DOMAIN_NAME>",
              "to": ["bar@example.com", "YOU@YOUR_DOMAIN_NAME"],
              "subject": "Hello",
              "text": "Testing some Mailgun awesomness!"})

You can also track events and lots more, see the quickstart guide.

I hope you find this useful!


回答 2

我想通过建议yagmail软件包来帮助您发送电子邮件(我是维护者,对不起广告,但是我觉得这真的可以帮到您!)。

您的整个代码将是:

import yagmail
yag = yagmail.SMTP(FROM, 'pass')
yag.send(TO, SUBJECT, TEXT)

请注意,我为所有参数提供了默认值,例如,如果您要发送给自己,则可以省略TO,如果您不希望使用主题,也可以将其省略。

此外,目标还在于使其真正易于附加html代码或图像(和其他文件)。

在放置内容的地方,您可以执行以下操作:

contents = ['Body text, and here is an embedded image:', 'http://somedomain/image.png',
            'You can also find an audio file attached.', '/local/path/song.mp3']

哇,发送附件有多容易!这将需要20行而不使用yagmail;)

另外,如果只设置一次,则无需再次输入密码(并安全地存储密码)。在您的情况下,您可以执行以下操作:

import yagmail
yagmail.SMTP().send(contents = contents)

更加简洁!

我邀请您浏览github或直接使用进行安装pip install yagmail

I’d like to help you with sending emails by advising the yagmail package (I’m the maintainer, sorry for the advertising, but I feel it can really help!).

The whole code for you would be:

import yagmail
yag = yagmail.SMTP(FROM, 'pass')
yag.send(TO, SUBJECT, TEXT)

Note that I provide defaults for all arguments, for example if you want to send to yourself, you can omit TO, if you don’t want a subject, you can omit it also.

Furthermore, the goal is also to make it really easy to attach html code or images (and other files).

Where you put contents you can do something like:

contents = ['Body text, and here is an embedded image:', 'http://somedomain/image.png',
            'You can also find an audio file attached.', '/local/path/song.mp3']

Wow, how easy it is to send attachments! This would take like 20 lines without yagmail ;)

Also, if you set it up once, you’ll never have to enter the password again (and have it safely stored). In your case you can do something like:

import yagmail
yagmail.SMTP().send(contents = contents)

which is much more concise!

I’d invite you to have a look at the github or install it directly with pip install yagmail.


回答 3

存在压痕问题。下面的代码将起作用:

import textwrap

def sendMail(FROM,TO,SUBJECT,TEXT,SERVER):
    import smtplib
    """this is some test documentation in the function"""
    message = textwrap.dedent("""\
        From: %s
        To: %s
        Subject: %s
        %s
        """ % (FROM, ", ".join(TO), SUBJECT, TEXT))
    # Send the mail
    server = smtplib.SMTP(SERVER)
    server.sendmail(FROM, TO, message)
    server.quit()

There is indentation problem. The code below will work:

import textwrap

def sendMail(FROM,TO,SUBJECT,TEXT,SERVER):
    import smtplib
    """this is some test documentation in the function"""
    message = textwrap.dedent("""\
        From: %s
        To: %s
        Subject: %s
        %s
        """ % (FROM, ", ".join(TO), SUBJECT, TEXT))
    # Send the mail
    server = smtplib.SMTP(SERVER)
    server.sendmail(FROM, TO, message)
    server.quit()


回答 4

这是Python上的示例3.x,比以下示例简单得多2.x

import smtplib
from email.message import EmailMessage
def send_mail(to_email, subject, message, server='smtp.example.cn',
              from_email='xx@example.com'):
    # import smtplib
    msg = EmailMessage()
    msg['Subject'] = subject
    msg['From'] = from_email
    msg['To'] = ', '.join(to_email)
    msg.set_content(message)
    print(msg)
    server = smtplib.SMTP(server)
    server.set_debuglevel(1)
    server.login(from_email, 'password')  # user & password
    server.send_message(msg)
    server.quit()
    print('successfully sent the mail.')

调用此函数:

send_mail(to_email=['12345@qq.com', '12345@126.com'],
          subject='hello', message='Your analysis has done!')

以下内容仅适用于中国用户:

如果使用126/163网状邮箱,则需要设置“客户端授权密码”,如下所示:

在此处输入图片说明

参考:https : //stackoverflow.com/a/41470149/2803344 https://docs.python.org/3/library/email.examples.html#email-examples

Here is an example on Python 3.x, much simpler than 2.x:

import smtplib
from email.message import EmailMessage
def send_mail(to_email, subject, message, server='smtp.example.cn',
              from_email='xx@example.com'):
    # import smtplib
    msg = EmailMessage()
    msg['Subject'] = subject
    msg['From'] = from_email
    msg['To'] = ', '.join(to_email)
    msg.set_content(message)
    print(msg)
    server = smtplib.SMTP(server)
    server.set_debuglevel(1)
    server.login(from_email, 'password')  # user & password
    server.send_message(msg)
    server.quit()
    print('successfully sent the mail.')

call this function:

send_mail(to_email=['12345@qq.com', '12345@126.com'],
          subject='hello', message='Your analysis has done!')

below may only for Chinese user:

If you use 126/163, 网易邮箱, you need to set”客户端授权密码”, like below:

enter image description here

ref: https://stackoverflow.com/a/41470149/2803344 https://docs.python.org/3/library/email.examples.html#email-examples


回答 5

在函数中缩进代码(可以)时,您也缩进了原始消息字符串的行。但是前导空格意味着标题行的折叠(串联),如RFC 2822-Internet消息格式的 2.2.3和3.2.3节所述:

从逻辑上讲,每个标题字段都是一行字符,包括字段名称,冒号和字段正文。但是,为了方便起见,并且为了处理每行998/78个字符的限制,可以将标头字段的字段正文部分拆分为多行表示;这称为“折叠”。

sendmail调用的函数形式中,所有行都以空格开头,因此是“展开”(串联)的,因此您尝试发送

From: monty@python.com    To: jon@mycompany.com    Subject: Hello!    This message was sent with Python's smtplib.

除了我们的想法之外,smtplib将不再理解To:and Subject:标头,因为这些名称仅在行首识别。而是smtplib假设发送者的电子邮件地址很长:

monty@python.com    To: jon@mycompany.com    Subject: Hello!    This message was sent with Python's smtplib.

这将无法正常工作,因此您的异常也随之而来。

解决方案很简单:只需保留message字符串即可。这可以通过一个函数(如Zeeshan建议的)或直接在源代码中完成:

import smtplib

def sendMail(FROM,TO,SUBJECT,TEXT,SERVER):
    """this is some test documentation in the function"""
    message = """\
From: %s
To: %s
Subject: %s

%s
""" % (FROM, ", ".join(TO), SUBJECT, TEXT)
    # Send the mail
    server = smtplib.SMTP(SERVER)
    server.sendmail(FROM, TO, message)
    server.quit()

现在展开不会发生,您发送

From: monty@python.com
To: jon@mycompany.com
Subject: Hello!

This message was sent with Python's smtplib.

这是行得通的,是您的旧代码完成的工作。

请注意,我还保留了标头和正文之间的空行以容纳RFC的 3.5节(这是必需的),并根据Python样式指南PEP-0008(可选)将include放在函数外部。

While indenting your code in the function (which is ok), you did also indent the lines of the raw message string. But leading white space implies folding (concatenation) of the header lines, as described in sections 2.2.3 and 3.2.3 of RFC 2822 – Internet Message Format:

Each header field is logically a single line of characters comprising the field name, the colon, and the field body. For convenience however, and to deal with the 998/78 character limitations per line, the field body portion of a header field can be split into a multiple line representation; this is called “folding”.

In the function form of your sendmail call, all lines are starting with white space and so are “unfolded” (concatenated) and you are trying to send

From: monty@python.com    To: jon@mycompany.com    Subject: Hello!    This message was sent with Python's smtplib.

Other than our mind suggests, smtplib will not understand the To: and Subject: headers any longer, because these names are only recognized at the beginning of a line. Instead smtplib will assume a very long sender email address:

monty@python.com    To: jon@mycompany.com    Subject: Hello!    This message was sent with Python's smtplib.

This won’t work and so comes your Exception.

The solution is simple: Just preserve the message string as it was before. This can be done by a function (as Zeeshan suggested) or right away in the source code:

import smtplib

def sendMail(FROM,TO,SUBJECT,TEXT,SERVER):
    """this is some test documentation in the function"""
    message = """\
From: %s
To: %s
Subject: %s

%s
""" % (FROM, ", ".join(TO), SUBJECT, TEXT)
    # Send the mail
    server = smtplib.SMTP(SERVER)
    server.sendmail(FROM, TO, message)
    server.quit()

Now the unfolding does not occur and you send

From: monty@python.com
To: jon@mycompany.com
Subject: Hello!

This message was sent with Python's smtplib.

which is what works and what was done by your old code.

Note that I was also preserving the empty line between headers and body to accommodate section 3.5 of the RFC (which is required) and put the include outside the function according to the Python style guide PEP-0008 (which is optional).


回答 6

可能是在您的消息中添加了标签。打印出消息,然后再将其传递给sendMail。

It’s probably putting tabs into your message. Print out message before you pass it to sendMail.


回答 7

确保您已授予发件人和收件人的权限,以发送电子邮件和接收来自电子邮件帐户中未知来源(外部来源)的电子邮件。

import smtplib

#Ports 465 and 587 are intended for email client to email server communication - sending email
server = smtplib.SMTP('smtp.gmail.com', 587)

#starttls() is a way to take an existing insecure connection and upgrade it to a secure connection using SSL/TLS.
server.starttls()

#Next, log in to the server
server.login("#email", "#password")

msg = "Hello! This Message was sent by the help of Python"

#Send the mail
server.sendmail("#Sender", "#Reciever", msg)

在此处输入图片说明

Make sure you have granted permission for both Sender and Receiver to send email and receive email from Unknown sources(External Sources) in Email Account.

import smtplib

#Ports 465 and 587 are intended for email client to email server communication - sending email
server = smtplib.SMTP('smtp.gmail.com', 587)

#starttls() is a way to take an existing insecure connection and upgrade it to a secure connection using SSL/TLS.
server.starttls()

#Next, log in to the server
server.login("#email", "#password")

msg = "Hello! This Message was sent by the help of Python"

#Send the mail
server.sendmail("#Sender", "#Reciever", msg)

enter image description here


回答 8

自从我刚弄清楚这是怎么回事以来,我以为我在这里输入了两位。

看来您在SERVER连接设置上没有指定端口,当我尝试连接到不使用默认端口25的SMTP服务器时,这对我有一点影响。

根据smtplib.SMTP文档,应该自动处理您的ehlo或helo请求/响应,因此您不必担心这一点(但是可以用来确认其他所有操作是否失败)。

另一个要问自己的问题是,您是否允许SMTP服务器本身上的SMTP连接?对于某些网站,例如GMAIL和ZOHO,您必须实际进入并激活电子邮件帐户中的IMAP连接。您的邮件服务器可能不允许不来自“ localhost”的SMTP连接?需要研究的东西。

最后一件事是您可能想尝试在TLS上启动连接。现在,大多数服务器都需要这种身份验证。

您会看到我在电子邮件中塞入了两个“收件人”字段。msg [‘TO’]和msg [‘FROM’] msg词典项目允许在电子邮件本身的标题中显示正确的信息,您可以在“收件人” /“发件人”字段中在电子邮件的接收端看到该标题(您可以甚至可以在此处添加“答复”字段。“收件人”和“发件人”字段本身就是服务器所需要的。

这是我在一个函数中使用的代码,它对我有用,它使用本地计算机和远程SMTP服务器(如所示的ZOHO)通过电子邮件发送* .txt文件的内容:

def emailResults(folder, filename):

    # body of the message
    doc = folder + filename + '.txt'
    with open(doc, 'r') as readText:
        msg = MIMEText(readText.read())

    # headers
    TO = 'to_user@domain.com'
    msg['To'] = TO
    FROM = 'from_user@domain.com'
    msg['From'] = FROM
    msg['Subject'] = 'email subject |' + filename

    # SMTP
    send = smtplib.SMTP('smtp.zoho.com', 587)
    send.starttls()
    send.login('from_user@domain.com', 'password')
    send.sendmail(FROM, TO, msg.as_string())
    send.quit()

Thought I’d put in my two bits here since I have just figured out how this works.

It appears that you don’t have the port specified on your SERVER connection settings, this effected me a little bit when I was trying to connect to my SMTP server that isn’t using the default port: 25.

According to the smtplib.SMTP docs, your ehlo or helo request/response should automatically be taken care of, so you shouldn’t have to worry about this (but might be something to confirm if all else fails).

Another thing to ask yourself is have you allowed SMTP connections on your SMTP server itself? For some sites like GMAIL and ZOHO you have to actually go in and activate the IMAP connections within the email account. Your mail server might not allow SMTP connections that don’t come from ‘localhost’ perhaps? Something to look into.

The final thing is you might want to try and initiate the connection on TLS. Most servers now require this type of authentication.

You’ll see I’ve jammed two TO fields into my email. The msg[‘TO’] and msg[‘FROM’] msg dictionary items allows the correct information to show up in the headers of the email itself, which one sees on the receiving end of the email in the To/From fields (you might even be able to add a Reply To field in here. The TO and FROM fields themselves are what the server requires. I know I’ve heard of some email servers rejecting emails if they don’t have the proper email headers in place.

This is the code I’ve used, in a function, that works for me to email the content of a *.txt file using my local computer and a remote SMTP server (ZOHO as shown):

def emailResults(folder, filename):

    # body of the message
    doc = folder + filename + '.txt'
    with open(doc, 'r') as readText:
        msg = MIMEText(readText.read())

    # headers
    TO = 'to_user@domain.com'
    msg['To'] = TO
    FROM = 'from_user@domain.com'
    msg['From'] = FROM
    msg['Subject'] = 'email subject |' + filename

    # SMTP
    send = smtplib.SMTP('smtp.zoho.com', 587)
    send.starttls()
    send.login('from_user@domain.com', 'password')
    send.sendmail(FROM, TO, msg.as_string())
    send.quit()

回答 9

值得注意的是,SMTP模块支持上下文管理器,因此不需要手动调用quit(),这将确保即使有异常也总是调用它。

    with smtplib.SMTP_SSL('smtp.gmail.com', 465) as server:
        server.ehlo()
        server.login(user, password)
        server.sendmail(from, to, body)

It’s worth noting that the SMTP module supports the context manager so there is no need to manually call quit(), this will guarantee it is always called even if there is an exception.

    with smtplib.SMTP_SSL('smtp.gmail.com', 465) as server:
        server.ehlo()
        server.login(user, password)
        server.sendmail(from, to, body)

回答 10

就您的代码而言,似乎没有什么根本上的错误,只是不清楚,您实际上是如何调用该函数的。我能想到的是,当服务器没有响应时,您将收到此SMTPServerDisconnected错误。如果您在smtplib(以下摘录)中查找getreply()函数,您将有所了解。

def getreply(self):
    """Get a reply from the server.

    Returns a tuple consisting of:

      - server response code (e.g. '250', or such, if all goes well)
        Note: returns -1 if it can't read response code.

      - server response string corresponding to response code (multiline
        responses are converted to a single, multiline string).

    Raises SMTPServerDisconnected if end-of-file is reached.
    """

请查看https://github.com/rreddy80/sendEmails/blob/master/sendEmailAttachments.py上的示例,如果您正在尝试这样做,该示例也使用函数调用来发送电子邮件(DRY方法)。

As far your code is concerned, there doesn’t seem to be anything fundamentally wrong with it except that, it is unclear how you’re actually calling that function. All I can think of is that when your server is not responding then you will get this SMTPServerDisconnected error. If you lookup the getreply() function in smtplib (excerpt below), you will get an idea.

def getreply(self):
    """Get a reply from the server.

    Returns a tuple consisting of:

      - server response code (e.g. '250', or such, if all goes well)
        Note: returns -1 if it can't read response code.

      - server response string corresponding to response code (multiline
        responses are converted to a single, multiline string).

    Raises SMTPServerDisconnected if end-of-file is reached.
    """

check an example at https://github.com/rreddy80/sendEmails/blob/master/sendEmailAttachments.py that also uses a function call to send an email, if that’s what you’re trying to do (DRY approach).


如何找到Python函数的参数数量?

问题:如何找到Python函数的参数数量?

如何找到Python函数的参数数量?我需要知道它有多少个普通参数以及多少个命名参数。

例:

def someMethod(self, arg1, kwarg1=None):
    pass

此方法有2个参数和1个命名参数。

How can I find the number of arguments of a Python function? I need to know how many normal arguments it has and how many named arguments.

Example:

def someMethod(self, arg1, kwarg1=None):
    pass

This method has 2 arguments and 1 named argument.


回答 0

于接受的答案已于弃用Python 3.0inspect.getargspec现在,您应该选择Signature取代它的类,而不是使用它。

创建功能的签名是通过简单的signature功能

from inspect import signature

def someMethod(self, arg1, kwarg1=None):
    pass

sig = signature(someMethod)

现在,您可以通过以下方式快速查看其参数str

str(sig)  # returns: '(self, arg1, kwarg1=None)'

或者您也可以通过来获取属性名称到参数对象的映射sig.parameters

params = sig.parameters 
print(params['kwarg1']) # prints: kwarg1=20

此外,你可以叫lensig.parameters也看到的参数此功能,需要数量:

print(len(params))  # 3

params映射中的每个条目实际上都是一个具有更多属性的Parameter对象,使您的生活更加轻松。例如,现在可以轻松地执行以下操作来获取参数并查看其默认值:

kwarg1 = params['kwarg1']
kwarg1.default # returns: None

包含在中的其余对象也类似parameters


对于Python 2.x用户,虽然inspect.getargspec 不建议弃用,但该语言很快就会成为:-)。该Signature类在该2.x系列中不可用,也不会提供。因此,您仍然需要使用inspect.getargspec

至于Python 2和3之间的转换,如果你有一些代码依赖的接口getargspec在Python 2切换到signature3是太难了,你必须将有价值的选择使用inspect.getfullargspec。它提供了与getargspec(单个可调用参数)相似的接口,以获取函数的参数,同时还处理某些其他情况,这些getargspec情况不是:

from inspect import getfullargspec

def someMethod(self, arg1, kwarg1=None):
    pass

args = getfullargspec(someMethod)

与一样getargspecgetfullargspec返回NamedTuple包含参数的。

print(args)
FullArgSpec(args=['self', 'arg1', 'kwarg1'], varargs=None, varkw=None, defaults=(None,), kwonlyargs=[], kwonlydefaults=None, annotations={})

The previously accepted answer has been deprecated as of Python 3.0. Instead of using inspect.getargspec you should now opt for the Signature class which superseded it.

Creating a Signature for the function is easy via the signature function:

from inspect import signature

def someMethod(self, arg1, kwarg1=None):
    pass

sig = signature(someMethod)

Now, you can either view its parameters quickly by string it:

str(sig)  # returns: '(self, arg1, kwarg1=None)'

or you can also get a mapping of attribute names to parameter objects via sig.parameters.

params = sig.parameters 
print(params['kwarg1']) # prints: kwarg1=20

Additionally, you can call len on sig.parameters to also see the number of arguments this function requires:

print(len(params))  # 3

Each entry in the params mapping is actually a Parameter object that has further attributes making your life easier. For example, grabbing a parameter and viewing its default value is now easily performed with:

kwarg1 = params['kwarg1']
kwarg1.default # returns: None

similarly for the rest of the objects contained in parameters.


As for Python 2.x users, while inspect.getargspec isn’t deprecated, the language will soon be :-). The Signature class isn’t available in the 2.x series and won’t be. So you still need to work with inspect.getargspec.

As for transitioning between Python 2 and 3, if you have code that relies on the interface of getargspec in Python 2 and switching to signature in 3 is too difficult, you do have the valuable option of using inspect.getfullargspec. It offers a similar interface to getargspec (a single callable argument) in order to grab the arguments of a function while also handling some additional cases that getargspec doesn’t:

from inspect import getfullargspec

def someMethod(self, arg1, kwarg1=None):
    pass

args = getfullargspec(someMethod)

As with getargspec, getfullargspec returns a NamedTuple which contains the arguments.

print(args)
FullArgSpec(args=['self', 'arg1', 'kwarg1'], varargs=None, varkw=None, defaults=(None,), kwonlyargs=[], kwonlydefaults=None, annotations={})

回答 1

import inspect
inspect.getargspec(someMethod)

参见检查模块

import inspect
inspect.getargspec(someMethod)

see the inspect module


回答 2

someMethod.func_code.co_argcount

或者,如果当前函数名称不确定,请执行以下操作:

import sys

sys._getframe().func_code.co_argcount
someMethod.func_code.co_argcount

or, if the current function name is undetermined:

import sys

sys._getframe().func_code.co_argcount

回答 3

inspect.getargspec()

获取函数参数的名称和默认值。返回包含四个内容的元组:(args,varargs,varkw,默认值)。args是参数名称的列表(它可能包含嵌套列表)。varargs和varkw是*和**参数的名称或无。defaults是默认参数值的元组;如果没有默认参数,则为None;如果该元组具有n个元素,则它们对应于args中列出的最后n个元素。

在2.6版中进行了更改:返回一个命名的元组ArgSpec(args,varargs,关键字,默认值)。

请参阅您可以列出关键字参数的python函数接收

inspect.getargspec()

Get the names and default values of a function’s arguments. A tuple of four things is returned: (args, varargs, varkw, defaults). args is a list of the argument names (it may contain nested lists). varargs and varkw are the names of the * and ** arguments or None. defaults is a tuple of default argument values or None if there are no default arguments; if this tuple has n elements, they correspond to the last n elements listed in args.

Changed in version 2.6: Returns a named tuple ArgSpec(args, varargs, keywords, defaults).

See can-you-list-the-keyword-arguments-a-python-function-receives.


回答 4

除此之外,我还看到大多数时候help()函数确实可以帮助您

例如,它提供了有关所采用参数的所有详细信息。

help(<method>)

给出以下

method(self, **kwargs) method of apiclient.discovery.Resource instance
Retrieves a report which is a collection of properties / statistics for a specific customer.

Args:
  date: string, Represents the date in yyyy-mm-dd format for which the data is to be fetched. (required)
  pageToken: string, Token to specify next page.
  parameters: string, Represents the application name, parameter name pairs to fetch in csv as app_name1:param_name1, app_name2:param_name2.

Returns:
  An object of the form:

    { # JSON template for a collection of usage reports.
    "nextPageToken": "A String", # Token for retrieving the next page
    "kind": "admin#reports#usageReports", # Th

Adding to the above, I’ve also seen that the most of the times help() function really helps

For eg, it gives all the details about the arguments it takes.

help(<method>)

gives the below

method(self, **kwargs) method of apiclient.discovery.Resource instance
Retrieves a report which is a collection of properties / statistics for a specific customer.

Args:
  date: string, Represents the date in yyyy-mm-dd format for which the data is to be fetched. (required)
  pageToken: string, Token to specify next page.
  parameters: string, Represents the application name, parameter name pairs to fetch in csv as app_name1:param_name1, app_name2:param_name2.

Returns:
  An object of the form:

    { # JSON template for a collection of usage reports.
    "nextPageToken": "A String", # Token for retrieving the next page
    "kind": "admin#reports#usageReports", # Th

回答 5

对于想要以可移植的方式在Python 2和Python 3.6+之间进行此操作的人们来说是个好消息:use inspect.getfullargspec()方法。它可以在Python 2.x和3.6+中使用

正如Jim Fasarakis Hilliard和其他人指出的那样,它以前是这样的:
1.在Python 2.x中:使用inspect.getargspec()
2.在Python 3.x中:使用签名,因为getargspec()getfullargspec()已弃用。

但是,从Python 3.6开始(受大众欢迎?),情况已经朝着更好的方向发展:

从Python 3 文档页面

inspect.getfullargspec(func)

在版本3.6中进行了更改:以前signature()在Python 3.5中记录了此方法的不推荐使用,但是为了恢复从原始getargspec()API 迁移来的单一源Python 2/3代码的明确支持的标准接口,该决定已被撤销。

Good news for folks who want to do this in a portable way between Python 2 and Python 3.6+: use inspect.getfullargspec() method. It works in both Python 2.x and 3.6+

As Jim Fasarakis Hilliard and others have pointed out, it used to be like this:
1. In Python 2.x: use inspect.getargspec()
2. In Python 3.x: use signature, as getargspec() and getfullargspec() were deprecated.

However, starting Python 3.6 (by popular demand?), things have changed towards better:

From the Python 3 documentation page:

inspect.getfullargspec(func)

Changed in version 3.6: This method was previously documented as deprecated in favour of signature() in Python 3.5, but that decision has been reversed in order to restore a clearly supported standard interface for single-source Python 2/3 code migrating away from the legacy getargspec() API.


回答 6

inspect.getargspec()满足您的需求

from inspect import getargspec

def func(a, b):
    pass
print len(getargspec(func).args)

inspect.getargspec() to meet your needs

from inspect import getargspec

def func(a, b):
    pass
print len(getargspec(func).args)

回答 7

正如其他答案所暗示的,getargspec只要被查询的东西实际上是一个功能,它就可以很好地工作。它不适用于,等内置函数openlen在以下情况下将引发异常:

TypeError: <built-in function open> is not a Python function

以下功能(受此答案启发)展示了一种解决方法。它返回预期的args数f

from inspect import isfunction, getargspec
def num_args(f):
  if isfunction(f):
    return len(getargspec(f).args)
  else:
    spec = f.__doc__.split('\n')[0]
    args = spec[spec.find('(')+1:spec.find(')')]
    return args.count(',')+1 if args else 0

这个想法是从__doc__字符串中解析出函数规范。显然,这依赖于所述字符串的格式,因此很难实现!

As other answers suggest, getargspec works well as long as the thing being queried is actually a function. It does not work for built-in functions such as open, len, etc, and will throw an exception in such cases:

TypeError: <built-in function open> is not a Python function

The below function (inspired by this answer) demonstrates a workaround. It returns the number of args expected by f:

from inspect import isfunction, getargspec
def num_args(f):
  if isfunction(f):
    return len(getargspec(f).args)
  else:
    spec = f.__doc__.split('\n')[0]
    args = spec[spec.find('(')+1:spec.find(')')]
    return args.count(',')+1 if args else 0

The idea is to parse the function spec out of the __doc__ string. Obviously this relies on the format of said string so is hardly robust!


回答 8

func.__code__.co_argcount给您任何数量的参数 之前 *args

func.__kwdefaults__给您关键字参数 之后的字典 *args

func.__code__.co_kwonlyargcount 等于 len(func.__kwdefaults__)

func.__defaults__为您提供出现在前面可选参数的值 *args

这是简单的例子:

插图

>>> def a(b, c, d, e, f=1, g=3, h=None, *i, j=2, k=3, **L):
    pass

>>> a.__code__.co_argcount
7
>>> a.__defaults__
(1, 3, None)
>>> len(a.__defaults__)
3
>>> 
>>> 
>>> a.__kwdefaults__
{'j': 2, 'k': 3}
>>> len(a.__kwdefaults__)
2
>>> a.__code__.co_kwonlyargcount
2

func.__code__.co_argcount gives you number of any arguments BEFORE *args

func.__kwdefaults__ gives you a dict of the keyword arguments AFTER *args

func.__code__.co_kwonlyargcount is equal to len(func.__kwdefaults__)

func.__defaults__ gives you the values of optional arguments that appear before *args

Here is the simple illustration:

the illustration

>>> def a(b, c, d, e, f=1, g=3, h=None, *i, j=2, k=3, **L):
    pass

>>> a.__code__.co_argcount
7
>>> a.__defaults__
(1, 3, None)
>>> len(a.__defaults__)
3
>>> 
>>> 
>>> a.__kwdefaults__
{'j': 2, 'k': 3}
>>> len(a.__kwdefaults__)
2
>>> a.__code__.co_kwonlyargcount
2

回答 9

在:

import inspect 

class X:
    def xyz(self, a, b, c): 
        return 

print(len(inspect.getfullargspec(X.xyz).args))

出:

4


注意:如果xyz不在X类之内,并且没有“自我”,而只有“ a,b,c”,那么它将打印3。

对于3.5以下的python,您可能需要在上面的代码中替换inspect.getfullargspecinspect.getargspec

In:

import inspect 

class X:
    def xyz(self, a, b, c): 
        return 

print(len(inspect.getfullargspec(X.xyz).args))

Out:

4


Note: If xyz wasn’t inside class X and had no “self” and just “a, b, c”, then it would have printed 3.

For python below 3.5, you may want to replace inspect.getfullargspec by inspect.getargspec in the code above.


在函数结束(例如,检查失败)之前,在python中退出函数(没有返回值)的最佳方法是什么?

问题:在函数结束(例如,检查失败)之前,在python中退出函数(没有返回值)的最佳方法是什么?

让我们假设一个迭代,其中我们调用一个没有返回值的函数。我认为我的程序应该表现的方式在以下伪代码中进行了解释:

for element in some_list:
    foo(element)

def foo(element):
    do something
    if check is true:
        do more (because check was succesful)
    else:
        return None
    do much much more...

如果我在python中实现此功能,则该函数返回一个None。是否有更好的方式“如果在函数主体中检查失败,则退出没有返回值的函数”?

Let’s assume an iteration in which we call a function without a return value. The way I think my program should behave is explained in this pseudocode:

for element in some_list:
    foo(element)

def foo(element):
    do something
    if check is true:
        do more (because check was succesful)
    else:
        return None
    do much much more...

If I implement this in python, it bothers me, that the function returns a None. Is there a better way for “exiting a function, that has no return value, if a check fails in the body of the function”?


回答 0

您可以简单地使用

return

与…完全相同

return None

None如果执行到达函数主体的末尾而没有命中return语句,则函数也将返回。不返回任何内容与None使用Python 返回相同。

You could simply use

return

which does exactly the same as

return None

Your function will also return None if execution reaches the end of the function body without hitting a return statement. Returning nothing is the same as returning None in Python.


回答 1

我会建议:

def foo(element):
    do something
    if not check: return
    do more (because check was succesful)
    do much much more...

I would suggest:

def foo(element):
    do something
    if not check: return
    do more (because check was succesful)
    do much much more...

回答 2

您可以使用return不带任何参数的语句退出函数

def foo(element):
    do something
    if check is true:
        do more (because check was succesful)
    else:
        return
    do much much more...

或引发异常,如果您想被告知该问题

def foo(element):
    do something
    if check is true:
        do more (because check was succesful)
    else:
        raise Exception("cause of the problem")
    do much much more...

you can use the return statement without any parameter to exit a function

def foo(element):
    do something
    if check is true:
        do more (because check was succesful)
    else:
        return
    do much much more...

or raise an exception if you want to be informed of the problem

def foo(element):
    do something
    if check is true:
        do more (because check was succesful)
    else:
        raise Exception("cause of the problem")
    do much much more...

Python3的“函数注释”有什么好的用处

问题:Python3的“函数注释”有什么好的用处

功能注释:PEP-3107

我碰到了一段代码,展示了Python3的功能注释。这个概念很简单,但是我想不起来为什么要用Python3来实现它们或对其有很好的用途。也许可以启发我吗?

这个怎么运作:

def foo(a: 'x', b: 5 + 6, c: list) -> max(2, 9):
    ... function body ...

在参数后冒号后面的所有内容均为“注释”,在后面的信息->为函数返回值的注释。

foo.func_annotations将返回一个字典:

{'a': 'x',
 'b': 11,
 'c': list,
 'return': 9}

拥有此功能有什么意义?

Function Annotations: PEP-3107

I ran across a snippet of code demonstrating Python3’s function annotations. The concept is simple but I can’t think of why these were implemented in Python3 or any good uses for them. Perhaps SO can enlighten me?

How it works:

def foo(a: 'x', b: 5 + 6, c: list) -> max(2, 9):
    ... function body ...

Everything following the colon after an argument is an ‘annotation’, and the information following the -> is an annotation for the function’s return value.

foo.func_annotations would return a dictionary:

{'a': 'x',
 'b': 11,
 'c': list,
 'return': 9}

What’s the significance of having this available?


回答 0

我认为这实际上很棒。

来自学术背景,我可以告诉您,注释已证明对启用像Java这样的语言的智能静态分析器非常有用。例如,您可以定义语义,例如状态限制,允许访问的线程,体系结构限制等,然后有很多工具可以读取这些内容并进行处理,以提供超出编译器的保证。您甚至可以编写检查前提条件/后置条件的东西。

我觉得这样的事情在Python中特别需要,因为它的输入较弱,但是实际上没有任何结构可以使它简单明了,并且成为正式语法的一部分。

注解还有其他用途,无法保证。我可以看到如何将基于Java的工具应用于Python。例如,我有一个工具,可让您为方法分配特殊警告,并在调用它们时向您提供指示,指示您应阅读其文档(例如,假设您有一个不能用负值调用的方法,但是从名称上不直观)。通过注释,我可以为Python技术性地编写类似的内容。同样,如果存在正式语法,则可以编写基于标签将大型方法组织起来的工具。

I think this is actually great.

Coming from an academic background, I can tell you that annotations have proved themselves invaluable for enabling smart static analyzers for languages like Java. For instance, you could define semantics like state restrictions, threads that are allowed to access, architecture limitations, etc., and there are quite a few tools that can then read these and process them to provide assurances beyond what you get from the compilers. You could even write things that check preconditions/postconditions.

I feel something like this is especially needed in Python because of its weaker typing, but there were really no constructs that made this straightforward and part of the official syntax.

There are other uses for annotations beyond assurance. I can see how I could apply my Java-based tools to Python. For instance, I have a tool that lets you assign special warnings to methods, and gives you indications when you call them that you should read their documentation (E.g., imagine you have a method that must not be invoked with a negative value, but it’s not intuitive from the name). With annotations, I could technicall write something like this for Python. Similarly, a tool that organizes methods in a large class based on tags can be written if there is an official syntax.


回答 1

函数批注就是您对它们所做的。

它们可以用于文档:

def kinetic_energy(mass: 'in kilograms', velocity: 'in meters per second'):
     ...

它们可用于前提条件检查:

def validate(func, locals):
    for var, test in func.__annotations__.items():
        value = locals[var]
        msg = 'Var: {0}\tValue: {1}\tTest: {2.__name__}'.format(var, value, test)
        assert test(value), msg


def is_int(x):
    return isinstance(x, int)

def between(lo, hi):
    def _between(x):
            return lo <= x <= hi
    return _between

def f(x: between(3, 10), y: is_int):
    validate(f, locals())
    print(x, y)


>>> f(0, 31.1)
Traceback (most recent call last):
   ... 
AssertionError: Var: y  Value: 31.1 Test: is_int

另请参阅http://www.python.org/dev/peps/pep-0362/了解实现类型检查的方法。

Function annotations are what you make of them.

They can be used for documentation:

def kinetic_energy(mass: 'in kilograms', velocity: 'in meters per second'):
     ...

They can be used for pre-condition checking:

def validate(func, locals):
    for var, test in func.__annotations__.items():
        value = locals[var]
        msg = 'Var: {0}\tValue: {1}\tTest: {2.__name__}'.format(var, value, test)
        assert test(value), msg


def is_int(x):
    return isinstance(x, int)

def between(lo, hi):
    def _between(x):
            return lo <= x <= hi
    return _between

def f(x: between(3, 10), y: is_int):
    validate(f, locals())
    print(x, y)


>>> f(0, 31.1)
Traceback (most recent call last):
   ... 
AssertionError: Var: y  Value: 31.1 Test: is_int

Also see http://www.python.org/dev/peps/pep-0362/ for a way to implement type checking.


回答 2

这是一个较晚的答案,但是AFAICT(当前对功能注释的最佳使用)是PEP-0484MyPy

Mypy是Python的可选静态类型检查器。您可以使用即将在Python 3.5 beta 1(PEP 484)中引入的类型注释标准,将类型提示添加到Python程序中,并使用mypy进行静态类型检查。

像这样使用:

from typing import Iterator

def fib(n: int) -> Iterator[int]:
    a, b = 0, 1
    while a < n:
        yield a
        a, b = b, a + b

This is a way late answer, but AFAICT, the best current use of function annotations is PEP-0484 and MyPy.

Mypy is an optional static type checker for Python. You can add type hints to your Python programs using the upcoming standard for type annotations introduced in Python 3.5 beta 1 (PEP 484), and use mypy to type check them statically.

Used like so:

from typing import Iterator

def fib(n: int) -> Iterator[int]:
    a, b = 0, 1
    while a < n:
        yield a
        a, b = b, a + b

回答 3

我想补充从我的回答很好地利用的一个具体的例子在这里,加上装饰可以做的多方法的简单机制。

# This is in the 'mm' module

registry = {}
import inspect

class MultiMethod(object):
    def __init__(self, name):
        self.name = name
        self.typemap = {}
    def __call__(self, *args):
        types = tuple(arg.__class__ for arg in args) # a generator expression!
        function = self.typemap.get(types)
        if function is None:
            raise TypeError("no match")
        return function(*args)
    def register(self, types, function):
        if types in self.typemap:
            raise TypeError("duplicate registration")
        self.typemap[types] = function

def multimethod(function):
    name = function.__name__
    mm = registry.get(name)
    if mm is None:
        mm = registry[name] = MultiMethod(name)
    spec = inspect.getfullargspec(function)
    types = tuple(spec.annotations[x] for x in spec.args)
    mm.register(types, function)
    return mm

以及使用示例:

from mm import multimethod

@multimethod
def foo(a: int):
    return "an int"

@multimethod
def foo(a: int, b: str):
    return "an int and a string"

if __name__ == '__main__':
    print("foo(1,'a') = {}".format(foo(1,'a')))
    print("foo(7) = {}".format(foo(7)))

可以通过将类型添加到装饰器上来完成,如Guido的原始文章所示,但是对参数本身进行注释会更好,因为这样可以避免错误地匹配参数和类型。

:在Python中,你可以访问注解function.__annotations__,而不是function.func_annotations因为func_*风格是关于Python 3去除。

Just to add a specific example of a good use from my answer here, coupled with decorators a simple mechanism for multimethods can be done.

# This is in the 'mm' module

registry = {}
import inspect

class MultiMethod(object):
    def __init__(self, name):
        self.name = name
        self.typemap = {}
    def __call__(self, *args):
        types = tuple(arg.__class__ for arg in args) # a generator expression!
        function = self.typemap.get(types)
        if function is None:
            raise TypeError("no match")
        return function(*args)
    def register(self, types, function):
        if types in self.typemap:
            raise TypeError("duplicate registration")
        self.typemap[types] = function

def multimethod(function):
    name = function.__name__
    mm = registry.get(name)
    if mm is None:
        mm = registry[name] = MultiMethod(name)
    spec = inspect.getfullargspec(function)
    types = tuple(spec.annotations[x] for x in spec.args)
    mm.register(types, function)
    return mm

and an example of use:

from mm import multimethod

@multimethod
def foo(a: int):
    return "an int"

@multimethod
def foo(a: int, b: str):
    return "an int and a string"

if __name__ == '__main__':
    print("foo(1,'a') = {}".format(foo(1,'a')))
    print("foo(7) = {}".format(foo(7)))

This can be done by adding the types to the decorator as Guido’s original post shows, but annotating the parameters themselves is better as it avoids the possibility of wrong matching of parameters and types.

Note: In Python you can access the annotations as function.__annotations__ rather than function.func_annotations as the func_* style was removed on Python 3.


回答 4

Uri已经给出了正确的答案,所以下面是一个不太严重的答案:这样您可以缩短文档字符串。

Uri has already given a proper answer, so here’s a less serious one: So you can make your docstrings shorter.


回答 5

第一次看到注释时,我以为“很棒!最后我可以选择进行类型检查!” 当然,我没有注意到注解实际上并没有执行。

因此,我决定编写一个简单的函数装饰器来实施它们

def ensure_annotations(f):
    from functools import wraps
    from inspect import getcallargs
    @wraps(f)
    def wrapper(*args, **kwargs):
        for arg, val in getcallargs(f, *args, **kwargs).items():
            if arg in f.__annotations__:
                templ = f.__annotations__[arg]
                msg = "Argument {arg} to {f} does not match annotation type {t}"
                Check(val).is_a(templ).or_raise(EnsureError, msg.format(arg=arg, f=f, t=templ))
        return_val = f(*args, **kwargs)
        if 'return' in f.__annotations__:
            templ = f.__annotations__['return']
            msg = "Return value of {f} does not match annotation type {t}"
            Check(return_val).is_a(templ).or_raise(EnsureError, msg.format(f=f, t=templ))
        return return_val
    return wrapper

@ensure_annotations
def f(x: int, y: float) -> float:
    return x+y

print(f(1, y=2.2))

>>> 3.2

print(f(1, y=2))

>>> ensure.EnsureError: Argument y to <function f at 0x109b7c710> does not match annotation type <class 'float'>

我已将其添加到“ 确保”库中。

The first time I saw annotations, I thought “great! Finally I can opt in to some type checking!” Of course, I hadn’t noticed that annotations are not actually enforced.

So I decided to write a simple function decorator to enforce them:

def ensure_annotations(f):
    from functools import wraps
    from inspect import getcallargs
    @wraps(f)
    def wrapper(*args, **kwargs):
        for arg, val in getcallargs(f, *args, **kwargs).items():
            if arg in f.__annotations__:
                templ = f.__annotations__[arg]
                msg = "Argument {arg} to {f} does not match annotation type {t}"
                Check(val).is_a(templ).or_raise(EnsureError, msg.format(arg=arg, f=f, t=templ))
        return_val = f(*args, **kwargs)
        if 'return' in f.__annotations__:
            templ = f.__annotations__['return']
            msg = "Return value of {f} does not match annotation type {t}"
            Check(return_val).is_a(templ).or_raise(EnsureError, msg.format(f=f, t=templ))
        return return_val
    return wrapper

@ensure_annotations
def f(x: int, y: float) -> float:
    return x+y

print(f(1, y=2.2))

>>> 3.2

print(f(1, y=2))

>>> ensure.EnsureError: Argument y to <function f at 0x109b7c710> does not match annotation type <class 'float'>

I added it to the Ensure library.


回答 6

自问起以来已经有很长时间了,但问题中给出的示例摘录(也如此处所述)来自PEP 3107,并且在thas PEP示例结尾处也给出了用例,它们可能会从PEP角度回答问题。查看;)

以下引自PEP3107

用例

在讨论注释的过程中,提出了许多用例。其中一些按其传达的信息进行分组。还包括可以利用注释的现有产品和包装的示例。

  • 提供打字信息
    • 类型检查([3],[4])
    • 让IDE显示函数期望和返回的类型([17])
    • 函数重载/泛型函数([22])
    • 外语桥梁([18],[19])
    • 改编([21],[20])
    • 谓词逻辑功能
    • 数据库查询映射
    • RPC参数封送([23])
  • 其他资讯
    • 参数和返回值的文档([24])

有关特定点(及其参考)的更多信息,请参见PEP

It a long time since this was asked but the example snippet given in the question is (as stated there as well) from PEP 3107 and at the end of thas PEP example Use cases are also given which might answer the question from the PEPs point of view ;)

The following is quoted from PEP3107

Use Cases

In the course of discussing annotations, a number of use-cases have been raised. Some of these are presented here, grouped by what kind of information they convey. Also included are examples of existing products and packages that could make use of annotations.

  • Providing typing information
    • Type checking ([3], [4])
    • Let IDEs show what types a function expects and returns ([17])
    • Function overloading / generic functions ([22])
    • Foreign-language bridges ([18], [19])
    • Adaptation ([21], [20])
    • Predicate logic functions
    • Database query mapping
    • RPC parameter marshaling ([23])
  • Other information
    • Documentation for parameters and return values ([24])

See the PEP for more information on specific points (as well as their references)


回答 7

Python 3.X(仅)还泛化了函数定义,以允许将参数和返回值与对象值一起注释以 用于扩展

用其META数据进行解释,以更明确地了解函数值。

注释的编码方式是:value在参数名称之后,默认值之前以及->value在参数列表之后。

它们被收集到__annotations__函数的属性中,但Python本身并未将其视为特殊的:

>>> def f(a:99, b:'spam'=None) -> float:
... print(a, b)
...
>>> f(88)
88 None
>>> f.__annotations__
{'a': 99, 'b': 'spam', 'return': <class 'float'>}

来源:Python Pocket Reference,第五版

例:

typeannotations模块提供了一组用于Python代码的类型检查和类型推断的工具。它还提供了一组用于注释功能和对象的类型。

这些工具主要设计用于静态分析器,如linter,代码完成库和IDE。另外,提供了用于进行运行时检查的装饰器。在Python中,运行时类型检查并不总是一个好主意,但在某些情况下,它可能非常有用。

https://github.com/ceronman/typeannotations

键入如何帮助编写更好的代码

键入可以帮助您进行静态代码分析,以在将代码发送到生产环境之前捕获类型错误,并防止出现一些明显的错误。有些工具例如mypy,可以将其添加到工具箱中,作为软件生命周期的一部分。mypy可以通过部分或完全针对您的代码库运行来检查类型是否正确。mypy还可以帮助您检测错误,例如从函数返回值时检查None类型。键入有助于使代码更整洁。您可以在不增加性能成本的情况下使用类型,而不必使用注释在文档字符串中指定类型的方式来记录代码。

干净的Python:Python中的优雅编码ISBN:ISBN-13(pbk):978-1-4842-4877-5

PEP 526-变量注释的语法

https://www.python.org/dev/peps/pep-0526/

https://www.attrs.org/en/stable/types.html

Python 3.X (only) also generalizes function definition to allow arguments and return values to be annotated with object values for use in extensions.

Its META-data to explain, to be more explicit about the function values.

Annotations are coded as :value after the argument name and before a default, and as ->value after the argument list.

They are collected into an __annotations__ attribute of the function, but are not otherwise treated as special by Python itself:

>>> def f(a:99, b:'spam'=None) -> float:
... print(a, b)
...
>>> f(88)
88 None
>>> f.__annotations__
{'a': 99, 'b': 'spam', 'return': <class 'float'>}

Source: Python Pocket Reference, Fifth Edition

EXAMPLE:

The typeannotations module provides a set of tools for type checking and type inference of Python code. It also a provides a set of types useful for annotating functions and objects.

These tools are mainly designed to be used by static analyzers such as linters, code completion libraries and IDEs. Additionally, decorators for making run-time checks are provided. Run-time type checking is not always a good idea in Python, but in some cases it can be very useful.

https://github.com/ceronman/typeannotations

How Typing Helps to Write Better Code

Typing can help you do static code analysis to catch type errors before you send your code to production and prevent you from some obvious bugs. There are tools like mypy, which you can add to your toolbox as part of your software life cycle. mypy can check for correct types by running against your codebase partially or fully. mypy also helps you to detect bugs such as checking for the None type when the value is returned from a function. Typing helps to make your code cleaner. Instead of documenting your code using comments, where you specify types in a docstring, you can use types without any performance cost.

Clean Python: Elegant Coding in Python ISBN: ISBN-13 (pbk): 978-1-4842-4877-5

PEP 526 — Syntax for Variable Annotations

https://www.python.org/dev/peps/pep-0526/

https://www.attrs.org/en/stable/types.html


回答 8

尽管在此描述了所有用法,但注释的一种可执行且最有可能的强制使用将是类型提示

目前尚未以任何方式强制执行此操作,但从PEP 484判断,Python的未来版本将仅允许类型作为注释的值。

引用注释的现有用法如何?

我们确实希望类型提示最终将成为注释的唯一用法,但这在使用Python 3.5首次键入类型模块之后,将需要进行额外的讨论和弃用期。当前的PEP将具有临时状态(请参阅PEP 411),直到发布Python 3.6。最快的可能方案将在3.6中引入对非类型提示注释的静默弃用,在3.7中引入完全弃用,并将类型提示声明为Python 3.8中唯一允许使用的注释。

尽管我还没有在3.6中看到任何过时的贬值,但是很可能会升至3.7。

因此,即使可能还有其他一些很好的用例,如果您不想在将来有此限制的情况下四处更改所有内容,最好还是仅将它们保留为类型提示。

Despite all uses described here, the one enforceable and, most likely, enforced use of annotations will be for type hints.

This is currently not enforced in any way but, judging from PEP 484, future versions of Python will only allow types as the value for annotations.

Quoting What about existing uses of annotations?:

We do hope that type hints will eventually become the sole use for annotations, but this will require additional discussion and a deprecation period after the initial roll-out of the typing module with Python 3.5. The current PEP will have provisional status (see PEP 411 ) until Python 3.6 is released. The fastest conceivable scheme would introduce silent deprecation of non-type-hint annotations in 3.6, full deprecation in 3.7, and declare type hints as the only allowed use of annotations in Python 3.8.

Though I haven’t seen any silent deprecations in 3.6 yet, this could very well be bumped to 3.7, instead.

So, even though there might be some other good use-cases, it is best to keep them solely for type hinting if you don’t want to go around changing everything in a future where this restriction is in place.


回答 9

作为一个延迟回答的问题,我的一些软件包(marrow.script,WebCore等)也使用了注释来声明类型转换(即,转换来自Web的传入值,检测哪些参数是布尔开关等)。以执行其他参数标记。

Marrow Script可为任意函数和类构建完整的命令行界面,并允许通过注释定义文档,强制转换和回调派生的默认值,并带有装饰器以支持较早的运行时。我所有使用注释的库都支持以下形式:

any_string  # documentation
any_callable  # typecast / callback, not called if defaulting
(any_callable, any_string)  # combination
AnnotationClass()  # package-specific rich annotation object
[AnnotationClass(), AnnotationClass(), …]  # cooperative annotation

对文档字符串或类型转换功能的“裸露”支持可简化与其他可识别注释的库的混合。(即,有一个使用类型转换的Web控制器,它也恰巧作为命令行脚本公开。)

编辑添加:我还开始使用TypeGuard包,该包使用开发时断言进行验证。好处:在启用“优化”(-O/ PYTHONOPTIMIZEenv var)的情况下运行时,可能会很昂贵(例如,递归)的检查被省略,因为您已经在开发中正确测试了应用程序,因此在生产中不必要检查。

As a bit of a delayed answer, several of my packages (marrow.script, WebCore, etc.) use annotations where available to declare typecasting (i.e. transforming incoming values from the web, detecting which arguments are boolean switches, etc.) as well as to perform additional markup of arguments.

Marrow Script builds a complete command-line interface to arbitrary functions and classes and allows for defining documentation, casting, and callback-derived default values via annotations, with a decorator to support older runtimes. All of my libraries that use annotations support the forms:

any_string  # documentation
any_callable  # typecast / callback, not called if defaulting
(any_callable, any_string)  # combination
AnnotationClass()  # package-specific rich annotation object
[AnnotationClass(), AnnotationClass(), …]  # cooperative annotation

“Bare” support for docstrings or typecasting functions allows for easier mixing with other libraries that are annotation-aware. (I.e. have a web controller using typecasting that also happens to be exposed as a command-line script.)

Edited to add: I’ve also begun making use of the TypeGuard package using development-time assertions for validation. Benefit: when run with “optimizations” enabled (-O / PYTHONOPTIMIZE env var) the checks, which may be expensive (e.g. recursive) are omitted, with the idea that you’ve properly tested your app in development so the checks should be unnecessary in production.


回答 10

注释可用于轻松地模块化代码。例如,我要维护的程序模块可以只定义以下方法:

def run(param1: int):
    """
    Does things.

    :param param1: Needed for counting.
    """
    pass

我们可以要求用户输入一个名为“ param1”的东西,该东西“需要计数”并且应该是“ int”。最后,我们甚至可以将用户提供的字符串转换为所需的类型,以获取最轻松的体验。

请参阅我们的函数元数据对象以获取开放源代码类,该类对此有所帮助,并且可以自动检索所需的值并将其转换为任何所需的类型(因为注释是一种转换方法)。甚至IDE都显示正确的自动完成功能,并假定类型符合注释-非常合适。

Annotations can be used for easily modularizing code. E.g. a module for a program which I’m maintaining could just define a method like:

def run(param1: int):
    """
    Does things.

    :param param1: Needed for counting.
    """
    pass

and we could ask the user for a thing named “param1” which is “Needed for counting” and should be an “int”. In the end we can even convert the string given by the user to the desired type to get the most hassle free experience.

See our function metadata object for an open source class which helps with this and can automatically retrieve needed values and convert them to any desired type (because the annotation is a conversion method). Even IDEs show autocompletions right and assume that types are according to annotations – a perfect fit.


回答 11

如果您查看Cython的好处列表,那么主要的一项功能就是能够告诉编译器Python对象的类型。

我可以预见一个未来,Cython(或编译某些Python代码的类似工具)将使用注释语法来发挥作用。

If you look at the list of benefits of Cython, a major one is the ability to tell the compiler which type a Python object is.

I can envision a future where Cython (or similar tools that compile some of your Python code) will use the annotation syntax to do their magic.


在python中使用参数列表调用函数

问题:在python中使用参数列表调用函数

我正在尝试在python中的另一个函数内调用一个函数,但是找不到正确的语法。我想做的是这样的:

def wrapper(func, args):
    func(args)

def func1(x):
    print(x)

def func2(x, y, z):
    return x+y+z

wrapper(func1, [x])
wrapper(func2, [x, y, z])

在这种情况下,第一个呼叫将起作用,而第二个则将不起作用。我要修改的是包装函数,而不是被调用的函数。

I’m trying to call a function inside another function in python, but can’t find the right syntax. What I want to do is something like this:

def wrapper(func, args):
    func(args)

def func1(x):
    print(x)

def func2(x, y, z):
    return x+y+z

wrapper(func1, [x])
wrapper(func2, [x, y, z])

In this case first call will work, and second won’t. What I want to modify is the wrapper function and not the called functions.


回答 0

为了扩大其他答案:

在该行中:

def wrapper(func, *args):

*旁边的* args表示“使用给出的其余参数并将它们放在一个称为args” 的列表中。

在该行中:

    func(*args)

args此处旁边的* 表示“将该列表称为args,并将其“解包”到其余参数中。

因此,您可以执行以下操作:

def wrapper1(func, *args): # with star
    func(*args)

def wrapper2(func, args): # without star
    func(*args)

def func2(x, y, z):
    print x+y+z

wrapper1(func2, 1, 2, 3)
wrapper2(func2, [1, 2, 3])

在中wrapper2,列表是显式传递的,但是在两个包装器中都args包含list [1,2,3]

To expand a little on the other answers:

In the line:

def wrapper(func, *args):

The * next to args means “take the rest of the parameters given and put them in a list called args“.

In the line:

    func(*args)

The * next to args here means “take this list called args and ‘unwrap’ it into the rest of the parameters.

So you can do the following:

def wrapper1(func, *args): # with star
    func(*args)

def wrapper2(func, args): # without star
    func(*args)

def func2(x, y, z):
    print x+y+z

wrapper1(func2, 1, 2, 3)
wrapper2(func2, [1, 2, 3])

In wrapper2, the list is passed explicitly, but in both wrappers args contains the list [1,2,3].


回答 1

包装函数的最简单方法

    func(*args, **kwargs)

…是手动编写一个包装,该包装将在其内部调用func()

    def wrapper(*args, **kwargs):
        # do something before
        try:
            return func(*a, **kwargs)
        finally:
            # do something after

在Python中,函数是一个对象,因此您可以将其名称作为另一个函数的参数传递并返回它。您还可以为任何函数anyFunc()编写包装器生成器:

    def wrapperGenerator(anyFunc, *args, **kwargs):
        def wrapper(*args, **kwargs):
            try:
                # do something before
                return anyFunc(*args, **kwargs)
            finally:
                #do something after
        return wrapper

另请注意,在Python中,当您不知道或不想命名函数的所有参数时,可以引用参数元组,以其名称表示,并在括号后加星号函数名称:

    *args

例如,您可以定义一个可以接受任意数量参数的函数:

    def testFunc(*args):
        print args    # prints the tuple of arguments

Python提供了对函数参数的进一步操作。您可以允许函数接受关键字参数。在函数体内,关键字参数保存在字典中。在函数名称后的括号中,此字典用两个星号表示,后面是字典的名称:

    **kwargs

一个类似的示例显示关键字参数字典:

    def testFunc(**kwargs):
        print kwargs    # prints the dictionary of keyword arguments

The simpliest way to wrap a function

    func(*args, **kwargs)

… is to manually write a wrapper that would call func() inside itself:

    def wrapper(*args, **kwargs):
        # do something before
        try:
            return func(*a, **kwargs)
        finally:
            # do something after

In Python function is an object, so you can pass it’s name as an argument of another function and return it. You can also write a wrapper generator for any function anyFunc():

    def wrapperGenerator(anyFunc, *args, **kwargs):
        def wrapper(*args, **kwargs):
            try:
                # do something before
                return anyFunc(*args, **kwargs)
            finally:
                #do something after
        return wrapper

Please also note that in Python when you don’t know or don’t want to name all the arguments of a function, you can refer to a tuple of arguments, which is denoted by its name, preceded by an asterisk in the parentheses after the function name:

    *args

For example you can define a function that would take any number of arguments:

    def testFunc(*args):
        print args    # prints the tuple of arguments

Python provides for even further manipulation on function arguments. You can allow a function to take keyword arguments. Within the function body the keyword arguments are held in a dictionary. In the parentheses after the function name this dictionary is denoted by two asterisks followed by the name of the dictionary:

    **kwargs

A similar example that prints the keyword arguments dictionary:

    def testFunc(**kwargs):
        print kwargs    # prints the dictionary of keyword arguments

回答 2

您可以对可变长度参数使用* args和** kwargs语法。

* args和** kwargs是什么意思?

并来自官方python教程

http://docs.python.org/dev/tutorial/controlflow.html#more-on-defining-functions

You can use *args and **kwargs syntax for variable length arguments.

What do *args and **kwargs mean?

And from the official python tutorial

http://docs.python.org/dev/tutorial/controlflow.html#more-on-defining-functions


回答 3

问题的字面答案(完全按照您的要求做,只更改包装器,而不更改函数或函数调用)只是为了改变行

func(args)

阅读

func(*args)

这告诉Python接受给定的列表(在本例中为args),并将其内容作为位置参数传递给函数。

这个技巧在函数调用的两个方面都起作用,因此定义的函数如下:

def func2(*args):
    return sum(args)

将能够接受与您抛出的一样多的位置参数,并将它们全部放入名为的列表中args

我希望这有助于澄清一些事情。请注意,使用dicts // keywords参数也可以使用**代替*

The literal answer to your question (to do exactly what you asked, changing only the wrapper, not the functions or the function calls) is simply to alter the line

func(args)

to read

func(*args)

This tells Python to take the list given (in this case, args) and pass its contents to the function as positional arguments.

This trick works on both “sides” of the function call, so a function defined like this:

def func2(*args):
    return sum(args)

would be able to accept as many positional arguments as you throw at it, and place them all into a list called args.

I hope this helps to clarify things a little. Note that this is all possible with dicts/keyword arguments as well, using ** instead of *.


回答 4

您需要使用参数解包..

def wrapper(func, *args):
    func(*args)

def func1(x):
    print(x)

def func2(x, y, z):
    print x+y+z

wrapper(func1, 1)
wrapper(func2, 1, 2, 3)

You need to use arguments unpacking..

def wrapper(func, *args):
    func(*args)

def func1(x):
    print(x)

def func2(x, y, z):
    print x+y+z

wrapper(func1, 1)
wrapper(func2, 1, 2, 3)

回答 5

对以前的答案进行了少量补充,因为我找不到问题的解决方案,这不值得提出一个新问题,而是将我引到了这里。

这里是一个小的代码段,它结合listszip()并且*args,以提供一个包装能够处理的功能与参数未知量的未知量。

def f1(var1, var2, var3):
    print(var1+var2+var3)

def f2(var1, var2):
    print(var1*var2)

def f3():
    print('f3, empty')

def wrapper(a,b, func_list, arg_list):
    print(a)
    for f,var in zip(func_list,arg_list):
        f(*var)
    print(b)

f_list = [f1, f2, f3]
a_list = [[1,2,3], [4,5], []]

wrapper('begin', 'end', f_list, a_list)

请记住,这zip()不能为不等长列表提供安全检查,请参阅zip迭代器在python中断言等长

A small addition to previous answers, since I couldn’t find a solution for a problem, which is not worth opening a new question, but led me here.

Here is a small code snippet, which combines lists, zip() and *args, to provide a wrapper that can deal with an unknown amount of functions with an unknown amount of arguments.

def f1(var1, var2, var3):
    print(var1+var2+var3)

def f2(var1, var2):
    print(var1*var2)

def f3():
    print('f3, empty')

def wrapper(a,b, func_list, arg_list):
    print(a)
    for f,var in zip(func_list,arg_list):
        f(*var)
    print(b)

f_list = [f1, f2, f3]
a_list = [[1,2,3], [4,5], []]

wrapper('begin', 'end', f_list, a_list)

Keep in mind, that zip() does not provide a safety check for lists of unequal length, see zip iterators asserting for equal length in python.


如何使用可选参数创建Python函数?

问题:如何使用可选参数创建Python函数?

我有一个带几个参数的Python函数。在某些情况下,可以忽略其中一些参数。

def some_function (self, a, b, c, d = None, e = None, f = None, g = None, h = None):
    #code

这些参数d通过h是字符串,每个都有不同的含义。我可以选择要以任何组合形式传递的可选参数,这一点很重要。例如,(a, b, C, d, e)(a, b, C, g, h),,或(a, b, C, d, e, f,或所有它们(这些是我的选择)。

如果我可以重载该函数,那就太好了-但我读到Python不支持重载。我试图在列表中插入一些必需的int参数-并收到参数不匹配错误。

现在,我正在发送空字符串来代替前几个缺少的参数作为占位符。我希望能够仅使用实际值来调用函数。

有什么办法吗?我可以传递一个列表而不是参数列表吗?

现在,使用ctypes的原型看起来像:

_fdll.some_function.argtypes = [c_void_p, c_char_p, c_int, c_char_p, c_char_p, c_char_p, c_char_p, c_char_p]

I have a Python function which takes several arguments. Some of these arguments could be omitted in some scenarios.

def some_function (self, a, b, c, d = None, e = None, f = None, g = None, h = None):
    #code

The arguments d through h are strings which each have different meanings. It is important that I can choose which optional parameters to pass in any combination. For example, (a, b, C, d, e), or (a, b, C, g, h), or (a, b, C, d, e, f, or all of them (these are my choices).

It would be great if I could overload the function – but I read that Python does not support overloading. I tried to insert some of the required int arguments in the list – and got an argument mismatch error.

Right now I am sending empty strings in place of the first few missing arguments as placeholders. I would like to be able to call a function just using actual values.

Is there any way to do this? Could I pass a list instead of the argument list?

Right now the prototype using ctypes looks something like:

_fdll.some_function.argtypes = [c_void_p, c_char_p, c_int, c_char_p, c_char_p, c_char_p, c_char_p, c_char_p]

回答 0

尝试像这样调用它:obj.some_function( '1', 2, '3', g="foo", h="bar" )。在所需的位置参数之后,可以按名称指定特定的可选参数。

Try calling it like: obj.some_function( '1', 2, '3', g="foo", h="bar" ). After the required positional arguments, you can specify specific optional arguments by name.


回答 1

只需使用*args参数,它可以让您在后面传递任意数量的参数a,b,c。您将不得不在映射中添加一些逻辑args-> c,d,e,f但它是过载的“方式”。

def myfunc(a,b, *args, **kwargs):
   for ar in args:
      print ar
myfunc(a,b,c,d,e,f)

它会打印出 c,d,e,f


同样,您可以使用kwargs参数,然后可以命名参数。

def myfunc(a,b, *args, **kwargs):
      c = kwargs.get('c', None)
      d = kwargs.get('d', None)
      #etc
myfunc(a,b, c='nick', d='dog', ...)

然后kwargs将有一个字典,其中包含所有在之后被键值的参数a,b

Just use the *args parameter, which allows you to pass as many arguments as you want after your a,b,c. You would have to add some logic to map args->c,d,e,f but its a “way” of overloading.

def myfunc(a,b, *args, **kwargs):
   for ar in args:
      print ar
myfunc(a,b,c,d,e,f)

And it will print values of c,d,e,f


Similarly you could use the kwargs argument and then you could name your parameters.

def myfunc(a,b, *args, **kwargs):
      c = kwargs.get('c', None)
      d = kwargs.get('d', None)
      #etc
myfunc(a,b, c='nick', d='dog', ...)

And then kwargs would have a dictionary of all the parameters that are key valued after a,b


为什么Python代码使用len()函数而不是length方法?

问题:为什么Python代码使用len()函数而不是length方法?

我知道python具有len()用于确定字符串大小的函数,但是我想知道为什么它不是字符串对象的方法。

更新资料

好吧,我意识到我很尴尬地犯了错误。__len__()实际上是字符串对象的方法。在字符串对象上使用len函数在Python中看到面向对象的代码似乎很奇怪。此外,看到__len__名字而不是len 也很奇怪。

I know that python has a len() function that is used to determine the size of a string, but I was wondering why it’s not a method of the string object.

Update

Ok, I realized I was embarrassingly mistaken. __len__() is actually a method of a string object. It just seems weird to see object oriented code in Python using the len function on string objects. Furthermore, it’s also weird to see __len__ as the name instead of just len.


回答 0

字符串确实有一个length方法: __len__()

Python中的协议是在具有一定长度并使用内置len()函数的对象上实现此方法,该内置函数会为您调用该方法,类似于您实现__iter__()和使用内置iter()函数的方法(或在后面调用方法)的场景)在可迭代的对象上。

有关更多信息,请参见模拟容器类型

这是有关Python协议主题的好书:Python和最小惊讶原则

Strings do have a length method: __len__()

The protocol in Python is to implement this method on objects which have a length and use the built-in len() function, which calls it for you, similar to the way you would implement __iter__() and use the built-in iter() function (or have the method called behind the scenes for you) on objects which are iterable.

See Emulating container types for more information.

Here’s a good read on the subject of protocols in Python: Python and the Principle of Least Astonishment


回答 1

吉姆对这个问题的回答可能会有所帮助。我在这里复制。引用Guido van Rossum:

首先,出于HCI的原因,我选择len(x)而不是x.len()(def __len __()来得很晚)。实际上,两个HCI相互交织在一起:

(a)对于某些运算,前缀表示法比后缀读得更好-前缀(和infix!)运算符在数学中有很长的传统,喜欢在视觉上帮助数学家思考问题的表示法。将我们将x *(a + b)之类的公式重写为x a + x b 的简便性与使用原始OO符号做相同事情的笨拙性进行比较。

(b)当我读到说len(x)的代码时,我知道它是在问某物的长度。这告诉我两件事:结果是整数,参数是某种容器。相反,当我阅读x.len()时,我必须已经知道x是某种实现接口或从具有标准len()的类继承的容器。当未实现映射的类具有get()或keys()方法,或者非文件类具有write()方法时,我们有时会感到困惑。

用另一种方式说同样的事情,我将“ len”视为内置操作。我不想失去那个。/…/

Jim’s answer to this question may help; I copy it here. Quoting Guido van Rossum:

First of all, I chose len(x) over x.len() for HCI reasons (def __len__() came much later). There are two intertwined reasons actually, both HCI:

(a) For some operations, prefix notation just reads better than postfix — prefix (and infix!) operations have a long tradition in mathematics which likes notations where the visuals help the mathematician thinking about a problem. Compare the easy with which we rewrite a formula like x*(a+b) into xa + xb to the clumsiness of doing the same thing using a raw OO notation.

(b) When I read code that says len(x) I know that it is asking for the length of something. This tells me two things: the result is an integer, and the argument is some kind of container. To the contrary, when I read x.len(), I have to already know that x is some kind of container implementing an interface or inheriting from a class that has a standard len(). Witness the confusion we occasionally have when a class that is not implementing a mapping has a get() or keys() method, or something that isn’t a file has a write() method.

Saying the same thing in another way, I see ‘len‘ as a built-in operation. I’d hate to lose that. /…/


回答 2

有一种len方法:

>>> a = 'a string of some length'
>>> a.__len__()
23
>>> a.__len__
<method-wrapper '__len__' of str object at 0x02005650>

There is a len method:

>>> a = 'a string of some length'
>>> a.__len__()
23
>>> a.__len__
<method-wrapper '__len__' of str object at 0x02005650>

回答 3

Python是一种务实的编程语言,并为原因len()是一个功能,而不是一个方法strlistdict等务实。

len()内置函数直接处理的内置类型:CPython的执行len()实际返回的值ob_size字段中PyVarObject的C结构代表任意可变大小的内置存储器中的对象。这是很多比调用一个方法快-无属性的查找需要发生。获取集合中的项目数是一种常见的操作,必须对这些基本类型多样为提高工作效率strlistarray.array等。

但是,为了提高一致性,当应用len(o)到用户定义的类型时,Python会o.__len__()作为后备调用。 __len____abs__和所有其他特殊的记录方法的Python数据模型可以很容易地创建对象,其行为像内置插件,使表现力和高度一致的API,我们称之为“Python化”。

通过实现特殊的方法,您的对象可以支持迭代,重载infix运算符,在with块中管理上下文等。您可以将数据模型视为一种使用Python语言本身作为框架的方式,您可以在其中无缝集成所创建的对象。

第二个原因,通过报价从吉多·范罗苏姆等支撑这一个,是它更容易阅读和写len(s)s.len()

该表示法len(s)与带有前缀表示法的一元运算符一致,例如abs(n)len()的使用频率比更高abs(),并且应该易于编写。

可能还有一个历史原因:在Python之前的ABC语言中(在其设计中很有影响力),有一个一元运算符,#s其含义为len(s)

Python is a pragmatic programming language, and the reasons for len() being a function and not a method of str, list, dict etc. are pragmatic.

The len() built-in function deals directly with built-in types: the CPython implementation of len() actually returns the value of the ob_size field in the PyVarObject C struct that represents any variable-sized built-in object in memory. This is much faster than calling a method — no attribute lookup needs to happen. Getting the number of items in a collection is a common operation and must work efficiently for such basic and diverse types as str, list, array.array etc.

However, to promote consistency, when applying len(o) to a user-defined type, Python calls o.__len__() as a fallback. __len__, __abs__ and all the other special methods documented in the Python Data Model make it easy to create objects that behave like the built-ins, enabling the expressive and highly consistent APIs we call “Pythonic”.

By implementing special methods your objects can support iteration, overload infix operators, manage contexts in with blocks etc. You can think of the Data Model as a way of using the Python language itself as a framework where the objects you create can be integrated seamlessly.

A second reason, supported by quotes from Guido van Rossum like this one, is that it is easier to read and write len(s) than s.len().

The notation len(s) is consistent with unary operators with prefix notation, like abs(n). len() is used way more often than abs(), and it deserves to be as easy to write.

There may also be a historical reason: in the ABC language which preceded Python (and was very influential in its design), there was a unary operator written as #s which meant len(s).


回答 4

met% python -c 'import this' | grep 'only one'
There should be one-- and preferably only one --obvious way to do it.
met% python -c 'import this' | grep 'only one'
There should be one-- and preferably only one --obvious way to do it.

回答 5

这里有一些很好的答案,因此在我给出自己的名字之前,我想重点介绍一下我在这里读过的一些宝石(无红宝石双关语)。

  • Python并不是纯粹的OOP语言,它是一种通用的多范式语言,它使程序员能够使用他们最熟悉的范式和/或最适合其解决方案的范式。
  • Python具有一流的功能,因此len实际上是一个对象。另一方面,Ruby没有一流的功能。因此,len函数对象具有自己的方法,可以通过运行进行检查dir(len)

如果您不喜欢此代码在自己的代码中的工作方式,那么使用首选方法重新实现容器是很简单的(请参见下面的示例)。

>>> class List(list):
...     def len(self):
...         return len(self)
...
>>> class Dict(dict):
...     def len(self):
...         return len(self)
...
>>> class Tuple(tuple):
...     def len(self):
...         return len(self)
...
>>> class Set(set):
...     def len(self):
...         return len(self)
...
>>> my_list = List([1,2,3,4,5,6,7,8,9,'A','B','C','D','E','F'])
>>> my_dict = Dict({'key': 'value', 'site': 'stackoverflow'})
>>> my_set = Set({1,2,3,4,5,6,7,8,9,'A','B','C','D','E','F'})
>>> my_tuple = Tuple((1,2,3,4,5,6,7,8,9,'A','B','C','D','E','F'))
>>> my_containers = Tuple((my_list, my_dict, my_set, my_tuple))
>>>
>>> for container in my_containers:
...     print container.len()
...
15
2
15
15

There are some great answers here, and so before I give my own I’d like to highlight a few of the gems (no ruby pun intended) I’ve read here.

  • Python is not a pure OOP language — it’s a general purpose, multi-paradigm language that allows the programmer to use the paradigm they are most comfortable with and/or the paradigm that is best suited for their solution.
  • Python has first-class functions, so len is actually an object. Ruby, on the other hand, doesn’t have first class functions. So the len function object has it’s own methods that you can inspect by running dir(len).

If you don’t like the way this works in your own code, it’s trivial for you to re-implement the containers using your preferred method (see example below).

>>> class List(list):
...     def len(self):
...         return len(self)
...
>>> class Dict(dict):
...     def len(self):
...         return len(self)
...
>>> class Tuple(tuple):
...     def len(self):
...         return len(self)
...
>>> class Set(set):
...     def len(self):
...         return len(self)
...
>>> my_list = List([1,2,3,4,5,6,7,8,9,'A','B','C','D','E','F'])
>>> my_dict = Dict({'key': 'value', 'site': 'stackoverflow'})
>>> my_set = Set({1,2,3,4,5,6,7,8,9,'A','B','C','D','E','F'})
>>> my_tuple = Tuple((1,2,3,4,5,6,7,8,9,'A','B','C','D','E','F'))
>>> my_containers = Tuple((my_list, my_dict, my_set, my_tuple))
>>>
>>> for container in my_containers:
...     print container.len()
...
15
2
15
15

回答 6

你也可以说

>> x = 'test'
>> len(x)
4

使用Python 2.7.3。

You can also say

>> x = 'test'
>> len(x)
4

Using Python 2.7.3.


回答 7

这里的其余答案缺少一些内容:len函数检查__len__方法是否返回非负数intlen作为函数的事实意味着类无法重写此行为以避免检查。因此,len(obj)给出了不能达到的安全级别obj.len()

例:

>>> class A:
...     def __len__(self):
...         return 'foo'
...
>>> len(A())
Traceback (most recent call last):
  File "<pyshell#8>", line 1, in <module>
    len(A())
TypeError: 'str' object cannot be interpreted as an integer
>>> class B:
...     def __len__(self):
...         return -1
... 
>>> len(B())
Traceback (most recent call last):
  File "<pyshell#13>", line 1, in <module>
    len(B())
ValueError: __len__() should return >= 0

当然,可以len通过将其重新分配为全局变量来“覆盖” 函数,但是比起覆盖类中方法的代码,这样做的代码明显更可疑。

Something missing from the rest of the answers here: the len function checks that the __len__ method returns a non-negative int. The fact that len is a function means that classes cannot override this behaviour to avoid the check. As such, len(obj) gives a level of safety that obj.len() cannot.

Example:

>>> class A:
...     def __len__(self):
...         return 'foo'
...
>>> len(A())
Traceback (most recent call last):
  File "<pyshell#8>", line 1, in <module>
    len(A())
TypeError: 'str' object cannot be interpreted as an integer
>>> class B:
...     def __len__(self):
...         return -1
... 
>>> len(B())
Traceback (most recent call last):
  File "<pyshell#13>", line 1, in <module>
    len(B())
ValueError: __len__() should return >= 0

Of course, it is possible to “override” the len function by reassigning it as a global variable, but code which does this is much more obviously suspicious than code which overrides a method in a class.


回答 8

不是吗

>>> "abc".__len__()
3

It doesn’t?

>>> "abc".__len__()
3

将带有参数的函数传递给Python中的另一个函数?

问题:将带有参数的函数传递给Python中的另一个函数?

是否可以将带有参数的函数传递给Python中的另一个函数?

说出类似的内容:

def perform(function):
    return function()

但是要传递的函数将具有以下参数:

action1()
action2(p)
action3(p,r)

Is it possible to pass functions with arguments to another function in Python?

Say for something like:

def perform(function):
    return function()

But the functions to be passed will have arguments like:

action1()
action2(p)
action3(p,r)

回答 0

你是这个意思吗

def perform( fun, *args ):
    fun( *args )

def action1( args ):
    something

def action2( args ):
    something

perform( action1 )
perform( action2, p )
perform( action3, p, r )

Do you mean this?

def perform( fun, *args ):
    fun( *args )

def action1( args ):
    something

def action2( args ):
    something

perform( action1 )
perform( action2, p )
perform( action3, p, r )

回答 1

这就是lambda的用途:

def Perform(f):
    f()

Perform(lambda: Action1())
Perform(lambda: Action2(p))
Perform(lambda: Action3(p, r))

This is what lambda is for:

def Perform(f):
    f()

Perform(lambda: Action1())
Perform(lambda: Action2(p))
Perform(lambda: Action3(p, r))

回答 2

您可以像这样从functools使用局部函数。

from functools import partial

def perform(f):
    f()

perform(Action1)
perform(partial(Action2, p))
perform(partial(Action3, p, r))

也适用于关键字

perform(partial(Action4, param1=p))

You can use the partial function from functools like so.

from functools import partial

def perform(f):
    f()

perform(Action1)
perform(partial(Action2, p))
perform(partial(Action3, p, r))

Also works with keywords

perform(partial(Action4, param1=p))

回答 3

使用functools.partial,而不是lambdas!而且Ofc Perform是一个无用的函数,您可以直接传递函数。

for func in [Action1, partial(Action2, p), partial(Action3, p, r)]:
  func()

Use functools.partial, not lambdas! And ofc Perform is a useless function, you can pass around functions directly.

for func in [Action1, partial(Action2, p), partial(Action3, p, r)]:
  func()

回答 4

(几个月后)一个很小的真实示例,其中lambda很有用,但部分没有用:
说您想要通过2维函数的各种1维横截面,例如穿过一排山丘的切片。
quadf( x, f )接受一维f并调用它x
称其为y = -1 0 1处的垂直切割和x = -1 0 1处的水平切割

fx1 = quadf( x, lambda x: f( x, 1 ))
fx0 = quadf( x, lambda x: f( x, 0 ))
fx_1 = quadf( x, lambda x: f( x, -1 ))
fxy = parabola( y, fx_1, fx0, fx1 )

f_1y = quadf( y, lambda y: f( -1, y ))
f0y = quadf( y, lambda y: f( 0, y ))
f1y = quadf( y, lambda y: f( 1, y ))
fyx = parabola( x, f_1y, f0y, f1y )

据我所知,partial不能这样做-

quadf( y, partial( f, x=1 ))
TypeError: f() got multiple values for keyword argument 'x'

(如何为此添加标签numpy,partial,lambda?)

(months later) a tiny real example where lambda is useful, partial not:
say you want various 1-dimensional cross-sections through a 2-dimensional function, like slices through a row of hills.
quadf( x, f ) takes a 1-d f and calls it for various x.
To call it for vertical cuts at y = -1 0 1 and horizontal cuts at x = -1 0 1,

fx1 = quadf( x, lambda x: f( x, 1 ))
fx0 = quadf( x, lambda x: f( x, 0 ))
fx_1 = quadf( x, lambda x: f( x, -1 ))
fxy = parabola( y, fx_1, fx0, fx1 )

f_1y = quadf( y, lambda y: f( -1, y ))
f0y = quadf( y, lambda y: f( 0, y ))
f1y = quadf( y, lambda y: f( 1, y ))
fyx = parabola( x, f_1y, f0y, f1y )

As far as I know, partial can’t do this —

quadf( y, partial( f, x=1 ))
TypeError: f() got multiple values for keyword argument 'x'

(How to add tags numpy, partial, lambda to this ?)


回答 5

这称为部分函数,​​至少有3种方法可以做到这一点。我最喜欢的方式是使用lambda,因为它避免了对额外程序包的依赖,并且最不冗长。假设您有一个函数,add(x, y)并且希望将add(3, y)其他函数作为参数传递,以便其他函数确定用于y

使用lambda

# generic function takes op and its argument
def runOp(op, val):
    return op(val)

# declare full function
def add(x, y):
    return x+y

# run example
def main():
    f = lambda y: add(3, y)
    result = runOp(f, 1) # is 4

创建自己的包装器

在这里,您需要创建一个返回部分函数的函数。这显然更加冗长。

# generic function takes op and its argument
def runOp(op, val):
    return op(val)

# declare full function
def add(x, y):
    return x+y

# declare partial function
def addPartial(x):
    def _wrapper(y):
        return add(x, y)
    return _wrapper

# run example
def main():
    f = addPartial(3)
    result = runOp(f, 1) # is 4

从functools使用部分

这几乎与lambda上面显示的相同。那我们为什么需要这个呢?有几个原因。简而言之,partial在某些情况下可能会更快(请参阅其实现),并且您可以将其用于早期绑定而不是lambda的后期绑定。

from functools import partial

# generic function takes op and its argument
def runOp(op, val):
    return op(val)

# declare full function
def add(x, y):
    return x+y

# run example
def main():
    f = partial(add, 3)
    result = runOp(f, 1) # is 4

This is called partial functions and there are at least 3 ways to do this. My favorite way is using lambda because it avoids dependency on extra package and is the least verbose. Assume you have a function add(x, y) and you want to pass add(3, y) to some other function as parameter such that the other function decides the value for y.

Use lambda

# generic function takes op and its argument
def runOp(op, val):
    return op(val)

# declare full function
def add(x, y):
    return x+y

# run example
def main():
    f = lambda y: add(3, y)
    result = runOp(f, 1) # is 4

Create Your Own Wrapper

Here you need to create a function that returns the partial function. This is obviously lot more verbose.

# generic function takes op and its argument
def runOp(op, val):
    return op(val)

# declare full function
def add(x, y):
    return x+y

# declare partial function
def addPartial(x):
    def _wrapper(y):
        return add(x, y)
    return _wrapper

# run example
def main():
    f = addPartial(3)
    result = runOp(f, 1) # is 4

Use partial from functools

This is almost identical to lambda shown above. Then why do we need this? There are few reasons. In short, partial might be bit faster in some cases (see its implementation) and that you can use it for early binding vs lambda’s late binding.

from functools import partial

# generic function takes op and its argument
def runOp(op, val):
    return op(val)

# declare full function
def add(x, y):
    return x+y

# run example
def main():
    f = partial(add, 3)
    result = runOp(f, 1) # is 4

回答 6

这是一种使用闭包的方法:

    def generate_add_mult_func(func):
        def function_generator(x):
            return reduce(func,range(1,x))
        return function_generator

    def add(x,y):
        return x+y

    def mult(x,y):
        return x*y

    adding=generate_add_mult_func(add)
    multiplying=generate_add_mult_func(mult)

    print adding(10)
    print multiplying(10)

Here is a way to do it with a closure:

    def generate_add_mult_func(func):
        def function_generator(x):
            return reduce(func,range(1,x))
        return function_generator

    def add(x,y):
        return x+y

    def mult(x,y):
        return x*y

    adding=generate_add_mult_func(add)
    multiplying=generate_add_mult_func(mult)

    print adding(10)
    print multiplying(10)