分类目录归档:知识问答

普通参数与关键字参数

问题:普通参数与关键字参数

“关键字参数”与常规参数有何不同?不能将所有参数都传递为name=value而不是使用位置语法吗?

How are “keyword arguments” different from regular arguments? Can’t all arguments be passed as name=value instead of using positional syntax?


回答 0

有两个相关的概念,都称为“ 关键字参数 ”。

在调用方(这是其他评论者提到的),您可以通过名称指定一些函数自变量。您必须在所有不带名称的参数(位置参数)之后提及它们,并且对于所有根本没有提及的参数,都必须有默认值

另一个概念是在函数定义方面:您可以定义一个按名称接受参数的函数-甚至不必指定这些名称是什么。这些是纯关键字参数,不能按位置传递。语法是

def my_function(arg1, arg2, **kwargs)

您传递给此函数的所有关键字参数都将放入名为的字典中kwargs。您可以在运行时检查此字典的键,如下所示:

def my_function(**kwargs):
    print str(kwargs)

my_function(a=12, b="abc")

{'a': 12, 'b': 'abc'}

There are two related concepts, both called “keyword arguments“.

On the calling side, which is what other commenters have mentioned, you have the ability to specify some function arguments by name. You have to mention them after all of the arguments without names (positional arguments), and there must be default values for any parameters which were not mentioned at all.

The other concept is on the function definition side: you can define a function that takes parameters by name — and you don’t even have to specify what those names are. These are pure keyword arguments, and can’t be passed positionally. The syntax is

def my_function(arg1, arg2, **kwargs)

Any keyword arguments you pass into this function will be placed into a dictionary named kwargs. You can examine the keys of this dictionary at run-time, like this:

def my_function(**kwargs):
    print str(kwargs)

my_function(a=12, b="abc")

{'a': 12, 'b': 'abc'}

回答 1

最后一种语言功能在区分上很重要。考虑以下功能:

def foo(*positional, **keywords):
    print "Positional:", positional
    print "Keywords:", keywords

*positional参数将存储传递给的所有位置参数foo(),对您可以提供的数量没有限制。

>>> foo('one', 'two', 'three')
Positional: ('one', 'two', 'three')
Keywords: {}

**keywords参数将存储任何关键字参数:

>>> foo(a='one', b='two', c='three')
Positional: ()
Keywords: {'a': 'one', 'c': 'three', 'b': 'two'}

当然,您可以同时使用两者:

>>> foo('one','two',c='three',d='four')
Positional: ('one', 'two')
Keywords: {'c': 'three', 'd': 'four'}

这些功能很少使用,但有时它们非常有用,并且重要的是要知道哪些参数是位置参数或关键字。

There is one last language feature where the distinction is important. Consider the following function:

def foo(*positional, **keywords):
    print "Positional:", positional
    print "Keywords:", keywords

The *positional argument will store all of the positional arguments passed to foo(), with no limit to how many you can provide.

>>> foo('one', 'two', 'three')
Positional: ('one', 'two', 'three')
Keywords: {}

The **keywords argument will store any keyword arguments:

>>> foo(a='one', b='two', c='three')
Positional: ()
Keywords: {'a': 'one', 'c': 'three', 'b': 'two'}

And of course, you can use both at the same time:

>>> foo('one','two',c='three',d='four')
Positional: ('one', 'two')
Keywords: {'c': 'three', 'd': 'four'}

These features are rarely used, but occasionally they are very useful, and it’s important to know which arguments are positional or keywords.


回答 2

使用关键字参数与普通参数一样,只是顺序无关紧要。例如,下面的两个函数调用是相同的:

def foo(bar, baz):
    pass

foo(1, 2)
foo(baz=2, bar=1)

Using keyword arguments is the same thing as normal arguments except order doesn’t matter. For example the two functions calls below are the same:

def foo(bar, baz):
    pass

foo(1, 2)
foo(baz=2, bar=1)

回答 3

位置参数

他们前面没有关键字。顺序很重要!

func(1,2,3, "foo")

关键字参数

他们在前面有关键字。它们可以是任何顺序!

func(foo="bar", baz=5, hello=123)

func(baz=5, foo="bar", hello=123)

您还应该知道,如果您使用默认参数并且忽略插入关键字,那么顺序将很重要!

def func(foo=1, baz=2, hello=3): ...
func("bar", 5, 123)

Positional Arguments

They have no keywords before them. The order is important!

func(1,2,3, "foo")

Keyword Arguments

They have keywords in the front. They can be in any order!

func(foo="bar", baz=5, hello=123)

func(baz=5, foo="bar", hello=123)

You should also know that if you use default arguments and neglect to insert the keywords, then the order will then matter!

def func(foo=1, baz=2, hello=3): ...
func("bar", 5, 123)

回答 4

有两种方法可以将参数值分配给函数参数。

  1. 按位置。位置参数没有关键字,而是首先分配的。

  2. 按关键字。关键字参数具有关键字,并且在位置参数之后排在第二位。

请注意,可以选择使用位置参数。

如果不使用位置参数,那么-是的- 编写的所有内容实际上都是关键字参数。

调用一个函数您对使用位置或关键字或混合物的决定。您可以根据需要选择所有关键字。我们中有些人没有做出选择,而是使用位置参数。

There are two ways to assign argument values to function parameters, both are used.

  1. By Position. Positional arguments do not have keywords and are assigned first.

  2. By Keyword. Keyword arguments have keywords and are assigned second, after positional arguments.

Note that you have the option to use positional arguments.

If you don’t use positional arguments, then — yes — everything you wrote turns out to be a keyword argument.

When you call a function you make a decision to use position or keyword or a mixture. You can choose to do all keywords if you want. Some of us do not make this choice and use positional arguments.


回答 5

令我感到惊讶的是,似乎没有人指出可以像这样通过字典来传递形式参数的键参数参数。

>>> def func(a='a', b='b', c='c', **kwargs):
...    print 'a:%s, b:%s, c:%s' % (a, b, c)
... 
>>> func()
a:a, b:b, c:c
>>> func(**{'a' : 'z', 'b':'q', 'c':'v'})
a:z, b:q, c:v
>>> 

I’m surprised that no one seems to have pointed out that one can pass a dictionary of keyed argument parameters, that satisfy the formal parameters, like so.

>>> def func(a='a', b='b', c='c', **kwargs):
...    print 'a:%s, b:%s, c:%s' % (a, b, c)
... 
>>> func()
a:a, b:b, c:c
>>> func(**{'a' : 'z', 'b':'q', 'c':'v'})
a:z, b:q, c:v
>>> 

回答 6

使用Python 3里,你可以有两个必需和非必需的关键字参数


可选:(为参数“ b”定义的默认值)

def func1(a, *, b=42):
    ...
func1(value_for_a) # b is optional and will default to 42

必需(未为参数“ b”定义默认值):

def func2(a, *, b):
    ... 
func2(value_for_a, b=21) # b is set to 21 by the function call
func2(value_for_a) # ERROR: missing 1 required keyword-only argument: 'b'`

如果彼此之间有许多相似的参数,尤其是当它们属于同一类型时,这会有所帮助,在这种情况下,我更喜欢使用命名参数,或者如果参数属于同一类,则我会创建一个自定义类。

Using Python 3 you can have both required and non-required keyword arguments:


Optional: (default value defined for param ‘b’)

def func1(a, *, b=42):
    ...
func1(value_for_a) # b is optional and will default to 42

Required (no default value defined for param ‘b’):

def func2(a, *, b):
    ... 
func2(value_for_a, b=21) # b is set to 21 by the function call
func2(value_for_a) # ERROR: missing 1 required keyword-only argument: 'b'`

This can help in cases where you have many similar arguments next to each other especially if they are of the same type, in that case I prefer using named arguments or I create a custom class if arguments belong together.


回答 7

令我惊讶的是,没有人提到您可以使用*argsand **kwargs从此站点)混合使用位置参数和关键字参数来进行类似的事情:

def test_var_kwargs(farg, **kwargs):
    print "formal arg:", farg
    for key in kwargs:
        print "another keyword arg: %s: %s" % (key, kwargs[key])

这使您可以使用任意关键字参数,这些参数可能包含您不想预先定义的键。

I’m surprised no one has mentioned the fact that you can mix positional and keyword arguments to do sneaky things like this using *args and **kwargs (from this site):

def test_var_kwargs(farg, **kwargs):
    print "formal arg:", farg
    for key in kwargs:
        print "another keyword arg: %s: %s" % (key, kwargs[key])

This allows you to use arbitrary keyword arguments that may have keys you don’t want to define upfront.


回答 8

我正在寻找一个使用类型注释的默认kwargs的示例:

def test_var_kwarg(a: str, b: str='B', c: str='', **kwargs) -> str:
     return ' '.join([a, b, c, str(kwargs)])

例:

>>> print(test_var_kwarg('A', c='okay'))
A B okay {}
>>> d = {'f': 'F', 'g': 'G'}
>>> print(test_var_kwarg('a', c='c', b='b', **d))
a b c {'f': 'F', 'g': 'G'}
>>> print(test_var_kwarg('a', 'b', 'c'))
a b c {}

I was looking for an example that had default kwargs using type annotation:

def test_var_kwarg(a: str, b: str='B', c: str='', **kwargs) -> str:
     return ' '.join([a, b, c, str(kwargs)])

example:

>>> print(test_var_kwarg('A', c='okay'))
A B okay {}
>>> d = {'f': 'F', 'g': 'G'}
>>> print(test_var_kwarg('a', c='c', b='b', **d))
a b c {'f': 'F', 'g': 'G'}
>>> print(test_var_kwarg('a', 'b', 'c'))
a b c {}

回答 9

只是补充/添加一种方法来定义调用函数时未在关键字中分配的参数默认值

def func(**keywargs):
if 'my_word' not in keywargs:
    word = 'default_msg'
else:
    word = keywargs['my_word']
return word

通过以下方式调用:

print(func())
print(func(my_word='love'))

你会得到:

default_msg
love

阅读更多关于*args**kwargsPython中:https://www.digitalocean.com/community/tutorials/how-to-use-args-and-kwargs-in-python-3

Just suplement/add a way for defining the default value of arguments that is not assigned in key words when calling the function:

def func(**keywargs):
if 'my_word' not in keywargs:
    word = 'default_msg'
else:
    word = keywargs['my_word']
return word

call this by:

print(func())
print(func(my_word='love'))

you’ll get:

default_msg
love

read more about *args and **kwargs in python: https://www.digitalocean.com/community/tutorials/how-to-use-args-and-kwargs-in-python-3


如何使用Python以提供者的身份通过Gmail发送电子邮件?

问题:如何使用Python以提供者的身份通过Gmail发送电子邮件?

我正在尝试使用python发送电子邮件(Gmail),但出现以下错误。

Traceback (most recent call last):  
File "emailSend.py", line 14, in <module>  
server.login(username,password)  
File "/usr/lib/python2.5/smtplib.py", line 554, in login  
raise SMTPException("SMTP AUTH extension not supported by server.")  
smtplib.SMTPException: SMTP AUTH extension not supported by server.

Python脚本如下。

import smtplib
fromaddr = 'user_me@gmail.com'
toaddrs  = 'user_you@gmail.com'
msg = 'Why,Oh why!'
username = 'user_me@gmail.com'
password = 'pwd'
server = smtplib.SMTP('smtp.gmail.com:587')
server.starttls()
server.login(username,password)
server.sendmail(fromaddr, toaddrs, msg)
server.quit()

I am trying to send email (Gmail) using python, but I am getting following error.

Traceback (most recent call last):  
File "emailSend.py", line 14, in <module>  
server.login(username,password)  
File "/usr/lib/python2.5/smtplib.py", line 554, in login  
raise SMTPException("SMTP AUTH extension not supported by server.")  
smtplib.SMTPException: SMTP AUTH extension not supported by server.

The Python script is the following.

import smtplib
fromaddr = 'user_me@gmail.com'
toaddrs  = 'user_you@gmail.com'
msg = 'Why,Oh why!'
username = 'user_me@gmail.com'
password = 'pwd'
server = smtplib.SMTP('smtp.gmail.com:587')
server.starttls()
server.login(username,password)
server.sendmail(fromaddr, toaddrs, msg)
server.quit()

回答 0

您需要先说一下EHLO才能直接进入STARTTLS

server = smtplib.SMTP('smtp.gmail.com:587')
server.ehlo()
server.starttls()

此外,你应该真正创造From:To:以及Subject:邮件标题,一个空行,并使用从邮件正文中分离出来CRLF作为EOL标志。

例如

msg = "\r\n".join([
  "From: user_me@gmail.com",
  "To: user_you@gmail.com",
  "Subject: Just a message",
  "",
  "Why, oh why"
  ])

You need to say EHLO before just running straight into STARTTLS:

server = smtplib.SMTP('smtp.gmail.com:587')
server.ehlo()
server.starttls()

Also you should really create From:, To: and Subject: message headers, separated from the message body by a blank line and use CRLF as EOL markers.

E.g.

msg = "\r\n".join([
  "From: user_me@gmail.com",
  "To: user_you@gmail.com",
  "Subject: Just a message",
  "",
  "Why, oh why"
  ])

回答 1

def send_email(user, pwd, recipient, subject, body):
    import smtplib

    FROM = user
    TO = recipient if isinstance(recipient, list) else [recipient]
    SUBJECT = subject
    TEXT = body

    # Prepare actual message
    message = """From: %s\nTo: %s\nSubject: %s\n\n%s
    """ % (FROM, ", ".join(TO), SUBJECT, TEXT)
    try:
        server = smtplib.SMTP("smtp.gmail.com", 587)
        server.ehlo()
        server.starttls()
        server.login(user, pwd)
        server.sendmail(FROM, TO, message)
        server.close()
        print 'successfully sent the mail'
    except:
        print "failed to send mail"

如果要使用端口465,则必须创建一个SMTP_SSL对象:

# SMTP_SSL Example
server_ssl = smtplib.SMTP_SSL("smtp.gmail.com", 465)
server_ssl.ehlo() # optional, called by login()
server_ssl.login(gmail_user, gmail_pwd)  
# ssl server doesn't support or need tls, so don't call server_ssl.starttls() 
server_ssl.sendmail(FROM, TO, message)
#server_ssl.quit()
server_ssl.close()
print 'successfully sent the mail'
def send_email(user, pwd, recipient, subject, body):
    import smtplib

    FROM = user
    TO = recipient if isinstance(recipient, list) else [recipient]
    SUBJECT = subject
    TEXT = body

    # Prepare actual message
    message = """From: %s\nTo: %s\nSubject: %s\n\n%s
    """ % (FROM, ", ".join(TO), SUBJECT, TEXT)
    try:
        server = smtplib.SMTP("smtp.gmail.com", 587)
        server.ehlo()
        server.starttls()
        server.login(user, pwd)
        server.sendmail(FROM, TO, message)
        server.close()
        print 'successfully sent the mail'
    except:
        print "failed to send mail"

if you want to use Port 465 you have to create an SMTP_SSL object:

# SMTP_SSL Example
server_ssl = smtplib.SMTP_SSL("smtp.gmail.com", 465)
server_ssl.ehlo() # optional, called by login()
server_ssl.login(gmail_user, gmail_pwd)  
# ssl server doesn't support or need tls, so don't call server_ssl.starttls() 
server_ssl.sendmail(FROM, TO, message)
#server_ssl.quit()
server_ssl.close()
print 'successfully sent the mail'

回答 2

我遇到了类似的问题,偶然发现了这个问题。我收到SMTP身份验证错误,但我的用户名/密码正确。这是修复它的原因。我读到这个:

https://support.google.com/accounts/answer/6010255

简而言之,Google不允许您通过smtplib登录,因为它已将这种登录标记为“安全性较低”,因此您要做的就是在登录google帐户时转到此链接,并允许访问:

https://www.google.com/settings/security/lesssecureapps

设置好之后(请参见下面的屏幕截图),它应该可以工作。

在此处输入图片说明

现在可以登录:

smtpserver = smtplib.SMTP("smtp.gmail.com", 587)
smtpserver.ehlo()
smtpserver.starttls()
smtpserver.ehlo()
smtpserver.login('me@gmail.com', 'me_pass')

更改后的响应:

(235, '2.7.0 Accepted')

事先回应:

smtplib.SMTPAuthenticationError: (535, '5.7.8 Username and Password not accepted. Learn more at\n5.7.8 http://support.google.com/mail/bin/answer.py?answer=14257 g66sm2224117qgf.37 - gsmtp')

还是行不通?如果仍然收到SMTPAuthenticationError,但现在的代码为534,则因为位置未知。点击此链接:

https://accounts.google.com/DisplayUnlockCaptcha

单击继续,这将给您10分钟的时间注册新应用。因此,现在就进行另一次登录尝试,它应该可以工作。

更新:这似乎无法立即生效,您可能会在smptlib中遇到此错误一段时间:

235 == 'Authentication successful'
503 == 'Error: already authenticated'

该消息显示使用浏览器登录:

SMTPAuthenticationError: (534, '5.7.9 Please log in with your web browser and then try again. Learn more at\n5.7.9 https://support.google.com/mail/bin/answer.py?answer=78754 qo11sm4014232igb.17 - gsmtp')

启用“ lesssecureapps”后,去喝杯咖啡,回来,然后再次尝试“ DisplayUnlockCaptcha”链接。根据用户的经验,更改可能最多需要一个小时才能生效。然后再次尝试登录过程。

I ran into a similar problem and stumbled on this question. I got an SMTP Authentication Error but my user name / pass was correct. Here is what fixed it. I read this:

https://support.google.com/accounts/answer/6010255

In a nutshell, google is not allowing you to log in via smtplib because it has flagged this sort of login as “less secure”, so what you have to do is go to this link while you’re logged in to your google account, and allow the access:

https://www.google.com/settings/security/lesssecureapps

Once that is set (see my screenshot below), it should work.

enter image description here

Login now works:

smtpserver = smtplib.SMTP("smtp.gmail.com", 587)
smtpserver.ehlo()
smtpserver.starttls()
smtpserver.ehlo()
smtpserver.login('me@gmail.com', 'me_pass')

Response after change:

(235, '2.7.0 Accepted')

Response prior:

smtplib.SMTPAuthenticationError: (535, '5.7.8 Username and Password not accepted. Learn more at\n5.7.8 http://support.google.com/mail/bin/answer.py?answer=14257 g66sm2224117qgf.37 - gsmtp')

Still not working? If you still get the SMTPAuthenticationError but now the code is 534, its because the location is unknown. Follow this link:

https://accounts.google.com/DisplayUnlockCaptcha

Click continue and this should give you 10 minutes for registering your new app. So proceed to doing another login attempt now and it should work.

UPDATE: This doesn’t seem to work right away you may be stuck for a while getting this error in smptlib:

235 == 'Authentication successful'
503 == 'Error: already authenticated'

The message says to use the browser to sign in:

SMTPAuthenticationError: (534, '5.7.9 Please log in with your web browser and then try again. Learn more at\n5.7.9 https://support.google.com/mail/bin/answer.py?answer=78754 qo11sm4014232igb.17 - gsmtp')

After enabling ‘lesssecureapps’, go for a coffee, come back, and try the ‘DisplayUnlockCaptcha’ link again. From user experience, it may take up to an hour for the change to kick in. Then try the sign-in process again.


回答 3

你对OOP失望吗?

#!/usr/bin/env python


import smtplib

class Gmail(object):
    def __init__(self, email, password):
        self.email = email
        self.password = password
        self.server = 'smtp.gmail.com'
        self.port = 587
        session = smtplib.SMTP(self.server, self.port)        
        session.ehlo()
        session.starttls()
        session.ehlo
        session.login(self.email, self.password)
        self.session = session

    def send_message(self, subject, body):
        ''' This must be removed '''
        headers = [
            "From: " + self.email,
            "Subject: " + subject,
            "To: " + self.email,
            "MIME-Version: 1.0",
           "Content-Type: text/html"]
        headers = "\r\n".join(headers)
        self.session.sendmail(
            self.email,
            self.email,
            headers + "\r\n\r\n" + body)


gm = Gmail('Your Email', 'Password')

gm.send_message('Subject', 'Message')

You down with OOP?

#!/usr/bin/env python


import smtplib

class Gmail(object):
    def __init__(self, email, password):
        self.email = email
        self.password = password
        self.server = 'smtp.gmail.com'
        self.port = 587
        session = smtplib.SMTP(self.server, self.port)        
        session.ehlo()
        session.starttls()
        session.ehlo
        session.login(self.email, self.password)
        self.session = session

    def send_message(self, subject, body):
        ''' This must be removed '''
        headers = [
            "From: " + self.email,
            "Subject: " + subject,
            "To: " + self.email,
            "MIME-Version: 1.0",
           "Content-Type: text/html"]
        headers = "\r\n".join(headers)
        self.session.sendmail(
            self.email,
            self.email,
            headers + "\r\n\r\n" + body)


gm = Gmail('Your Email', 'Password')

gm.send_message('Subject', 'Message')

回答 4

这个作品

创建Gmail APP密码!

创建之后,请创建一个名为 sendgmail.py

然后添加以下代码

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# =============================================================================
# Created By  : Jeromie Kirchoff
# Created Date: Mon Aug 02 17:46:00 PDT 2018
# =============================================================================
# Imports
# =============================================================================
import smtplib

# =============================================================================
# SET EMAIL LOGIN REQUIREMENTS
# =============================================================================
gmail_user = 'THEFROM@gmail.com'
gmail_app_password = 'YOUR-GOOGLE-APPLICATION-PASSWORD!!!!'

# =============================================================================
# SET THE INFO ABOUT THE SAID EMAIL
# =============================================================================
sent_from = gmail_user
sent_to = ['THE-TO@gmail.com', 'THE-TO@gmail.com']
sent_subject = "Where are all my Robot Women at?"
sent_body = ("Hey, what's up? friend!\n\n"
             "I hope you have been well!\n"
             "\n"
             "Cheers,\n"
             "Jay\n")

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

%s
""" % (sent_from, ", ".join(sent_to), sent_subject, sent_body)

# =============================================================================
# SEND EMAIL OR DIE TRYING!!!
# Details: http://www.samlogic.net/articles/smtp-commands-reference.htm
# =============================================================================

try:
    server = smtplib.SMTP_SSL('smtp.gmail.com', 465)
    server.ehlo()
    server.login(gmail_user, gmail_app_password)
    server.sendmail(sent_from, sent_to, email_text)
    server.close()

    print('Email sent!')
except Exception as exception:
    print("Error: %s!\n\n" % exception)

因此,如果成功,将看到如下图像:

我通过向自己发送电子邮件和向自己发送电子邮件进行了测试。

发送成功的电子邮件。

注意:我的帐户启用了两步验证。应用密码与此配合使用!(对于gmail smtp设置,您必须转到https://support.google.com/accounts/answer/185833?hl=zh_CN并执行以下步骤)

此设置不适用于启用了两步验证的帐户。此类帐户需要特定于应用程序的密码,以降低安全性。

安全性较低的应用访问权限...此设置不适用于启用了两步验证的帐户。

This Works

Create Gmail APP Password!

After you create that then create a file called sendgmail.py

Then add this code:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# =============================================================================
# Created By  : Jeromie Kirchoff
# Created Date: Mon Aug 02 17:46:00 PDT 2018
# =============================================================================
# Imports
# =============================================================================
import smtplib

# =============================================================================
# SET EMAIL LOGIN REQUIREMENTS
# =============================================================================
gmail_user = 'THEFROM@gmail.com'
gmail_app_password = 'YOUR-GOOGLE-APPLICATION-PASSWORD!!!!'

# =============================================================================
# SET THE INFO ABOUT THE SAID EMAIL
# =============================================================================
sent_from = gmail_user
sent_to = ['THE-TO@gmail.com', 'THE-TO@gmail.com']
sent_subject = "Where are all my Robot Women at?"
sent_body = ("Hey, what's up? friend!\n\n"
             "I hope you have been well!\n"
             "\n"
             "Cheers,\n"
             "Jay\n")

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

%s
""" % (sent_from, ", ".join(sent_to), sent_subject, sent_body)

# =============================================================================
# SEND EMAIL OR DIE TRYING!!!
# Details: http://www.samlogic.net/articles/smtp-commands-reference.htm
# =============================================================================

try:
    server = smtplib.SMTP_SSL('smtp.gmail.com', 465)
    server.ehlo()
    server.login(gmail_user, gmail_app_password)
    server.sendmail(sent_from, sent_to, email_text)
    server.close()

    print('Email sent!')
except Exception as exception:
    print("Error: %s!\n\n" % exception)

So, if you are successful, will see an image like this:

I tested by sending an email from and to myself.

Successful email sent.

Note: I have 2-Step Verification enabled on my account. App Password works with this! (for gmail smtp setup, you must go to https://support.google.com/accounts/answer/185833?hl=en and follow the below steps)

This setting is not available for accounts with 2-Step Verification enabled. Such accounts require an application-specific password for less secure apps access.

Less secure app access... This setting is not available for accounts with 2-Step Verification enabled.


回答 5

您可以在这里找到它:http : //jayrambhia.com/blog/send-emails-using-python

smtp_host = 'smtp.gmail.com'
smtp_port = 587
server = smtplib.SMTP()
server.connect(smtp_host,smtp_port)
server.ehlo()
server.starttls()
server.login(user,passw)
fromaddr = raw_input('Send mail by the name of: ')
tolist = raw_input('To: ').split()
sub = raw_input('Subject: ')

msg = email.MIMEMultipart.MIMEMultipart()
msg['From'] = fromaddr
msg['To'] = email.Utils.COMMASPACE.join(tolist)
msg['Subject'] = sub  
msg.attach(MIMEText(raw_input('Body: ')))
msg.attach(MIMEText('\nsent via python', 'plain'))
server.sendmail(user,tolist,msg.as_string())

You can find it here: http://jayrambhia.com/blog/send-emails-using-python

smtp_host = 'smtp.gmail.com'
smtp_port = 587
server = smtplib.SMTP()
server.connect(smtp_host,smtp_port)
server.ehlo()
server.starttls()
server.login(user,passw)
fromaddr = raw_input('Send mail by the name of: ')
tolist = raw_input('To: ').split()
sub = raw_input('Subject: ')

msg = email.MIMEMultipart.MIMEMultipart()
msg['From'] = fromaddr
msg['To'] = email.Utils.COMMASPACE.join(tolist)
msg['Subject'] = sub  
msg.attach(MIMEText(raw_input('Body: ')))
msg.attach(MIMEText('\nsent via python', 'plain'))
server.sendmail(user,tolist,msg.as_string())

回答 6

没有直接关系,但仍然值得指出的是,我的程序包试图使发送Gmail消息真正快速而轻松。它还尝试维护错误列表,并尝试立即指向解决方案。

实际上,只需要此代码即可完成您编写的操作:

import yagmail
yag = yagmail.SMTP('user_me@gmail.com')
yag.send('user_you@gmail.com', 'Why,Oh why!')

或一行代码:

yagmail.SMTP('user_me@gmail.com').send('user_you@gmail.com', 'Why,Oh why!')

对于软件包/安装,请查看gitpip,它们可用于Python 2和3。

Not directly related but still worth pointing out is that my package tries to make sending gmail messages really quick and painless. It also tries to maintain a list of errors and tries to point to the solution immediately.

It would literally only need this code to do exactly what you wrote:

import yagmail
yag = yagmail.SMTP('user_me@gmail.com')
yag.send('user_you@gmail.com', 'Why,Oh why!')

Or a one liner:

yagmail.SMTP('user_me@gmail.com').send('user_you@gmail.com', 'Why,Oh why!')

For the package/installation please look at git or pip, available for both Python 2 and 3.


回答 7

这是一个Gmail API示例。尽管更复杂,但这是我发现的唯一一种在2019年有效的方法。该示例摘自并修改自:

https://developers.google.com/gmail/api/guides/sending

您需要通过其网站使用Google API接口创建一个项目。接下来,您需要为您的应用启用GMAIL API。创建凭据,然后下载这些凭据,将其另存为凭据.json。

import pickle
import os.path
from googleapiclient.discovery import build
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request

from email.mime.text import MIMEText
import base64

#pip install --upgrade google-api-python-client google-auth-httplib2 google-auth-oauthlib

# If modifying these scopes, delete the file token.pickle.
SCOPES = ['https://www.googleapis.com/auth/gmail.readonly', 'https://www.googleapis.com/auth/gmail.send']

def create_message(sender, to, subject, msg):
    message = MIMEText(msg)
    message['to'] = to
    message['from'] = sender
    message['subject'] = subject

    # Base 64 encode
    b64_bytes = base64.urlsafe_b64encode(message.as_bytes())
    b64_string = b64_bytes.decode()
    return {'raw': b64_string}
    #return {'raw': base64.urlsafe_b64encode(message.as_string())}

def send_message(service, user_id, message):
    #try:
    message = (service.users().messages().send(userId=user_id, body=message).execute())
    print( 'Message Id: %s' % message['id'] )
    return message
    #except errors.HttpError, error:print( 'An error occurred: %s' % error )

def main():
    """Shows basic usage of the Gmail API.
    Lists the user's Gmail labels.
    """
    creds = None
    # The file token.pickle stores the user's access and refresh tokens, and is
    # created automatically when the authorization flow completes for the first
    # time.
    if os.path.exists('token.pickle'):
        with open('token.pickle', 'rb') as token:
            creds = pickle.load(token)
    # If there are no (valid) credentials available, let the user log in.
    if not creds or not creds.valid:
        if creds and creds.expired and creds.refresh_token:
            creds.refresh(Request())
        else:
            flow = InstalledAppFlow.from_client_secrets_file(
                'credentials.json', SCOPES)
            creds = flow.run_local_server(port=0)
        # Save the credentials for the next run
        with open('token.pickle', 'wb') as token:
            pickle.dump(creds, token)

    service = build('gmail', 'v1', credentials=creds)

    # Example read operation
    results = service.users().labels().list(userId='me').execute()
    labels = results.get('labels', [])

    if not labels:
        print('No labels found.')
    else:
        print('Labels:')
    for label in labels:
        print(label['name'])

    # Example write
    msg = create_message("from@gmail.com", "to@gmail.com", "Subject", "Msg")
    send_message( service, 'me', msg)

if __name__ == '__main__':
    main()

Here is a Gmail API example. Although more complicated, this is the only method I found that works in 2019. This example was taken and modified from:

https://developers.google.com/gmail/api/guides/sending

You’ll need create a project with Google’s API interfaces through their website. Next you’ll need to enable the GMAIL API for your app. Create credentials and then download those creds, save it as credentials.json.

import pickle
import os.path
from googleapiclient.discovery import build
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request

from email.mime.text import MIMEText
import base64

#pip install --upgrade google-api-python-client google-auth-httplib2 google-auth-oauthlib

# If modifying these scopes, delete the file token.pickle.
SCOPES = ['https://www.googleapis.com/auth/gmail.readonly', 'https://www.googleapis.com/auth/gmail.send']

def create_message(sender, to, subject, msg):
    message = MIMEText(msg)
    message['to'] = to
    message['from'] = sender
    message['subject'] = subject

    # Base 64 encode
    b64_bytes = base64.urlsafe_b64encode(message.as_bytes())
    b64_string = b64_bytes.decode()
    return {'raw': b64_string}
    #return {'raw': base64.urlsafe_b64encode(message.as_string())}

def send_message(service, user_id, message):
    #try:
    message = (service.users().messages().send(userId=user_id, body=message).execute())
    print( 'Message Id: %s' % message['id'] )
    return message
    #except errors.HttpError, error:print( 'An error occurred: %s' % error )

def main():
    """Shows basic usage of the Gmail API.
    Lists the user's Gmail labels.
    """
    creds = None
    # The file token.pickle stores the user's access and refresh tokens, and is
    # created automatically when the authorization flow completes for the first
    # time.
    if os.path.exists('token.pickle'):
        with open('token.pickle', 'rb') as token:
            creds = pickle.load(token)
    # If there are no (valid) credentials available, let the user log in.
    if not creds or not creds.valid:
        if creds and creds.expired and creds.refresh_token:
            creds.refresh(Request())
        else:
            flow = InstalledAppFlow.from_client_secrets_file(
                'credentials.json', SCOPES)
            creds = flow.run_local_server(port=0)
        # Save the credentials for the next run
        with open('token.pickle', 'wb') as token:
            pickle.dump(creds, token)

    service = build('gmail', 'v1', credentials=creds)

    # Example read operation
    results = service.users().labels().list(userId='me').execute()
    labels = results.get('labels', [])

    if not labels:
        print('No labels found.')
    else:
        print('Labels:')
    for label in labels:
        print(label['name'])

    # Example write
    msg = create_message("from@gmail.com", "to@gmail.com", "Subject", "Msg")
    send_message( service, 'me', msg)

if __name__ == '__main__':
    main()

回答 8

现在有一个gmail API,可让您通过REST发送电子邮件,阅读电子邮件和创建草稿。与SMTP调用不同,它是非阻塞的,这对于基于线程的Web服务器在请求线程中发送电子邮件(例如python Web服务器)可能是一件好事。该API也非常强大。

  • 当然,应该将电子邮件传递到非Web服务器队列,但是有选项很高兴。

如果您对域具有Google Apps管理员权限,那么设置起来最容易,因为这样您就可以授予客户一揽子许可。否则,您必须摆弄OAuth身份验证和权限。

这是一个证明它的要点:

https://gist.github.com/timrichardson/1154e29174926e462b7a

There is a gmail API now, which lets you send email, read email and create drafts via REST. Unlike the SMTP calls, it is non-blocking which can be a good thing for thread-based webservers sending email in the request thread (like python webservers). The API is also quite powerful.

  • Of course, email should be handed off to a non-webserver queue, but it’s nice to have options.

It’s easiest to setup if you have Google Apps administrator rights on the domain, because then you can give blanket permission to your client. Otherwise you have to fiddle with OAuth authentication and permission.

Here is a gist demonstrating it:

https://gist.github.com/timrichardson/1154e29174926e462b7a


回答 9

@David的好答案,这是针对不带通用try-except的Python 3的:

def send_email(user, password, recipient, subject, body):

    gmail_user = user
    gmail_pwd = password
    FROM = user
    TO = recipient if type(recipient) is list else [recipient]
    SUBJECT = subject
    TEXT = body

    # Prepare actual message
    message = """From: %s\nTo: %s\nSubject: %s\n\n%s
    """ % (FROM, ", ".join(TO), SUBJECT, TEXT)

    server = smtplib.SMTP("smtp.gmail.com", 587)
    server.ehlo()
    server.starttls()
    server.login(gmail_user, gmail_pwd)
    server.sendmail(FROM, TO, message)
    server.close()

great answer from @David, here is for Python 3 without the generic try-except:

def send_email(user, password, recipient, subject, body):

    gmail_user = user
    gmail_pwd = password
    FROM = user
    TO = recipient if type(recipient) is list else [recipient]
    SUBJECT = subject
    TEXT = body

    # Prepare actual message
    message = """From: %s\nTo: %s\nSubject: %s\n\n%s
    """ % (FROM, ", ".join(TO), SUBJECT, TEXT)

    server = smtplib.SMTP("smtp.gmail.com", 587)
    server.ehlo()
    server.starttls()
    server.login(gmail_user, gmail_pwd)
    server.sendmail(FROM, TO, message)
    server.close()

回答 10

在您的gmail帐户上启用安全性较低的应用并使用(Python> = 3.6):

import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText

gmailUser = 'XXXXX@gmail.com'
gmailPassword = 'XXXXX'
recipient = 'XXXXX@gmail.com'

message = f"""
Type your message here...
"""

msg = MIMEMultipart()
msg['From'] = f'"Your Name" <{gmailUser}>'
msg['To'] = recipient
msg['Subject'] = "Subject here..."
msg.attach(MIMEText(message))

try:
    mailServer = smtplib.SMTP('smtp.gmail.com', 587)
    mailServer.ehlo()
    mailServer.starttls()
    mailServer.ehlo()
    mailServer.login(gmailUser, gmailPassword)
    mailServer.sendmail(gmailUser, recipient, msg.as_string())
    mailServer.close()
    print ('Email sent!')
except:
    print ('Something went wrong...')

Enable less secure apps on your gmail account and use (Python>=3.6):

import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText

gmailUser = 'XXXXX@gmail.com'
gmailPassword = 'XXXXX'
recipient = 'XXXXX@gmail.com'

message = f"""
Type your message here...
"""

msg = MIMEMultipart()
msg['From'] = f'"Your Name" <{gmailUser}>'
msg['To'] = recipient
msg['Subject'] = "Subject here..."
msg.attach(MIMEText(message))

try:
    mailServer = smtplib.SMTP('smtp.gmail.com', 587)
    mailServer.ehlo()
    mailServer.starttls()
    mailServer.ehlo()
    mailServer.login(gmailUser, gmailPassword)
    mailServer.sendmail(gmailUser, recipient, msg.as_string())
    mailServer.close()
    print ('Email sent!')
except:
    print ('Something went wrong...')

回答 11

好像是老问题了smtplib。在python2.7一切正常。

更新:是的,server.ehlo()也可以提供帮助。

Seems like problem of the old smtplib. In python2.7 everything works fine.

Update: Yep, server.ehlo() also could help.


回答 12

    import smtplib

    fromadd='from@gmail.com'
    toadd='send@gmail.com'

    msg='''hi,how r u'''
    username='abc@gmail.com'
    passwd='password'

    try:
        server = smtplib.SMTP('smtp.gmail.com:587')
        server.ehlo()
        server.starttls()
        server.login(username,passwd)

        server.sendmail(fromadd,toadd,msg)
        print("Mail Send Successfully")
        server.quit()

   except:
        print("Error:unable to send mail")

   NOTE:https://www.google.com/settings/security/lesssecureapps that                                                         should be enabled
    import smtplib

    fromadd='from@gmail.com'
    toadd='send@gmail.com'

    msg='''hi,how r u'''
    username='abc@gmail.com'
    passwd='password'

    try:
        server = smtplib.SMTP('smtp.gmail.com:587')
        server.ehlo()
        server.starttls()
        server.login(username,passwd)

        server.sendmail(fromadd,toadd,msg)
        print("Mail Send Successfully")
        server.quit()

   except:
        print("Error:unable to send mail")

   NOTE:https://www.google.com/settings/security/lesssecureapps that                                                         should be enabled

回答 13

import smtplib
server = smtplib.SMTP('smtp.gmail.com', 587)
server.ehlo()
server.starttls()
server.login("fromaddress", "password")
msg = "HI!"
server.sendmail("fromaddress", "receiveraddress", msg)
server.quit()
import smtplib
server = smtplib.SMTP('smtp.gmail.com', 587)
server.ehlo()
server.starttls()
server.login("fromaddress", "password")
msg = "HI!"
server.sendmail("fromaddress", "receiveraddress", msg)
server.quit()

UnicodeDecodeError:’utf8’编解码器无法解码字节0x9c

问题:UnicodeDecodeError:’utf8’编解码器无法解码字节0x9c

我有一个套接字服务器,应该从客户端接收UTF-8有效字符。

问题是某些客户端(主要是黑客)正在通过它发送所有错误的数据。

我可以轻松地区分真正的客户端,但是我会将所有发送的数据记录到文件中,以便以后进行分析。

有时我会得到这样的œ导致UnicodeDecodeError错误的字符。

我需要使字符串UTF-8带有或不带有这些字符。


更新:

对于我的特殊情况,套接字服务是MTA,因此我只希望接收ASCII命令,例如:

EHLO example.com
MAIL FROM: <john.doe@example.com>
...

我将所有这些都记录在JSON中。

然后,一些没有好主意的人决定出售各种垃圾。

这就是为什么对于我的特定情况,完全可以剥离非ASCII字符。

I have a socket server that is supposed to receive UTF-8 valid characters from clients.

The problem is some clients (mainly hackers) are sending all the wrong kind of data over it.

I can easily distinguish the genuine client, but I am logging to files all the data sent so I can analyze it later.

Sometimes I get characters like this œ that cause the UnicodeDecodeError error.

I need to be able to make the string UTF-8 with or without those characters.


Update:

For my particular case the socket service was an MTA and thus I only expect to receive ASCII commands such as:

EHLO example.com
MAIL FROM: <john.doe@example.com>
...

I was logging all of this in JSON.

Then some folks out there without good intentions decided to send all kind of junk.

That is why for my specific case it is perfectly OK to strip the non ASCII characters.


回答 0

http://docs.python.org/howto/unicode.html#the-unicode-type

str = unicode(str, errors='replace')

要么

str = unicode(str, errors='ignore')

注意: 这将删除(忽略)有问题的字符,并返回不包含这些字符的字符串。

对我而言,这是理想的情况,因为我将其用作针对非ASCII输入的保护,这是我的应用程序所不允许的。

或者:使用codecs模块中的open方法读取文件:

import codecs
with codecs.open(file_name, 'r', encoding='utf-8',
                 errors='ignore') as fdata:

http://docs.python.org/howto/unicode.html#the-unicode-type

str = unicode(str, errors='replace')

or

str = unicode(str, errors='ignore')

Note: This will strip out (ignore) the characters in question returning the string without them.

For me this is ideal case since I’m using it as protection against non-ASCII input which is not allowed by my application.

Alternatively: Use the open method from the codecs module to read in the file:

import codecs
with codecs.open(file_name, 'r', encoding='utf-8',
                 errors='ignore') as fdata:

回答 1

将引擎从C更改为Python确实帮了我大忙。

引擎是C:

pd.read_csv(gdp_path, sep='\t', engine='c')

‘utf-8’编解码器无法解码位置18的字节0x92:无效的起始字节

引擎是Python:

pd.read_csv(gdp_path, sep='\t', engine='python')

对我来说没有错误。

Changing the engine from C to Python did the trick for me.

Engine is C:

pd.read_csv(gdp_path, sep='\t', engine='c')

‘utf-8’ codec can’t decode byte 0x92 in position 18: invalid start byte

Engine is Python:

pd.read_csv(gdp_path, sep='\t', engine='python')

No errors for me.


回答 2

现在,我开始使用Python 3时,这种类型的问题就冒出来了。我不知道Python 2只是在蒸腾文件编码方面的任何问题。

在上述方法都不适合我之后,我找到了关于差异以及如何找到解决方案的很好的解释。

http://python-notes.curiousefficiency.org/en/latest/python3/text_file_processing.html

简而言之,要使Python 3的行为与Python 2尽可能相似,请使用:

with open(filename, encoding="latin-1") as datafile:
    # work on datafile here

但是,请阅读文章,没有一种适合所有解决方案的尺寸。

This type of issue crops up for me now that I’ve moved to Python 3. I had no idea Python 2 was simply steam rolling any issues with file encoding.

I found this nice explanation of the differences and how to find a solution after none of the above worked for me.

http://python-notes.curiousefficiency.org/en/latest/python3/text_file_processing.html

In short, to make Python 3 behave as similarly as possible to Python 2 use:

with open(filename, encoding="latin-1") as datafile:
    # work on datafile here

However, read the article, there is no one size fits all solution.


回答 3

>>> '\x9c'.decode('cp1252')
u'\u0153'
>>> print '\x9c'.decode('cp1252')
œ
>>> '\x9c'.decode('cp1252')
u'\u0153'
>>> print '\x9c'.decode('cp1252')
œ

回答 4

我有同样的问题,UnicodeDecodeError我用这条线解决了。不知道这是否是最好的方法,但是对我有用。

str = str.decode('unicode_escape').encode('utf-8')

I had same problem with UnicodeDecodeError and i solved it with this line. Don’t know if is the best way but it worked for me.

str = str.decode('unicode_escape').encode('utf-8')

回答 5

首先,使用get_encoding_type来获取编码的文件类型:

import os    
from chardet import detect

# get file encoding type
def get_encoding_type(file):
    with open(file, 'rb') as f:
        rawdata = f.read()
    return detect(rawdata)['encoding']

第二,打开以下类型的文件:

open(current_file, 'r', encoding = get_encoding_type, errors='ignore')

the first,Using get_encoding_type to get the files type of encode:

import os    
from chardet import detect

# get file encoding type
def get_encoding_type(file):
    with open(file, 'rb') as f:
        rawdata = f.read()
    return detect(rawdata)['encoding']

the second, opening the files with the type:

open(current_file, 'r', encoding = get_encoding_type, errors='ignore')

回答 6

万一有人有同样的问题。我将Vim与YouCompleteMe一起使用,无法通过此错误消息启动ycmd,我所做的是:export LC_CTYPE="en_US.UTF-8",问题消失了。

Just in case of someone has the same problem. I’am using vim with YouCompleteMe, failed to start ycmd with this error message, what I did is: export LC_CTYPE="en_US.UTF-8", the problem is gone.


回答 7

如果需要对文件进行更改但不知道文件的编码,该怎么办?如果您知道编码是ASCII兼容的,并且只想检查或修改ASCII部分,则可以使用surrogateescape错误处理程序打开文件:

with open(fname, 'r', encoding="ascii", errors="surrogateescape") as f:
    data = f.read()

What can you do if you need to make a change to a file, but don’t know the file’s encoding? If you know the encoding is ASCII-compatible and only want to examine or modify the ASCII parts, you can open the file with the surrogateescape error handler:

with open(fname, 'r', encoding="ascii", errors="surrogateescape") as f:
    data = f.read()

回答 8

我已经解决了这个问题,只需添加

df = pd.read_csv(fileName,encoding='latin1')

I have solved this problem just by adding

df = pd.read_csv(fileName,encoding='latin1')

如何在python解释器中执行文件?

问题:如何在python解释器中执行文件?

我正在尝试从解释器中使用python命令执行文件。

编辑:我正在尝试使用该文件中的变量和设置,而不是调用一个单独的进程。

I’m trying to execute a file with python commands from within the interpreter.

EDIT: I’m trying to use variables and settings from that file, not to invoke a separate process.


回答 0

几种方法。

从外壳

python someFile.py

从IDLE内部,按F5

如果您是交互式输入,请尝试以下操作:(仅适用于Python 2!)

>>> variables= {}
>>> execfile( "someFile.py", variables )
>>> print variables # globals from the someFile module

对于Python3,请使用:

>>> exec(open("filename.py").read())

Several ways.

From the shell

python someFile.py

From inside IDLE, hit F5.

If you’re typing interactively, try this: (Python 2 only!)

>>> variables= {}
>>> execfile( "someFile.py", variables )
>>> print variables # globals from the someFile module

For Python3, use:

>>> exec(open("filename.py").read())

回答 1

对于Python 2:

>>> execfile('filename.py')

对于Python 3:

>>> exec(open("filename.py").read())
# or
>>> from pathlib import Path
>>> exec(Path("filename.py").read_text())

请参阅文档。如果您使用的是Python 3.0,请参见此问题

有关执行后如何从filename.py访问全局变量的示例,请参见@ S.Lott的答案。

For Python 2:

>>> execfile('filename.py')

For Python 3:

>>> exec(open("filename.py").read())
# or
>>> from pathlib import Path
>>> exec(Path("filename.py").read_text())

See the documentation. If you are using Python 3.0, see this question.

See answer by @S.Lott for an example of how you access globals from filename.py after executing it.


回答 2

Python 2 + Python 3

exec(open("./path/to/script.py").read(), globals())

这将执行一个脚本并将其所有全局变量放入解释器的全局范围(在大多数脚本环境中是正常行为)。

Python 3 exec文档

Python 2 + Python 3

exec(open("./path/to/script.py").read(), globals())

This will execute a script and put all it’s global variables in the interpreter’s global scope (the normal behavior in most scripting environments).

Python 3 exec Documentation


回答 3

感到惊讶的是我还没有看到这个。您可以执行一个文件,然后在执行终止后使用以下-i选项使解释器保持打开状态:

| foo.py |
----------
testvar = 10

def bar(bing):
  return bing*3

--------



$ python -i foo.py
>>> testvar 
10
>>> bar(6)
18

Surprised I haven’t seen this yet. You can execute a file and then leave the interpreter open after execution terminates using the -i option:

| foo.py |
----------
testvar = 10

def bar(bing):
  return bing*3

--------



$ python -i foo.py
>>> testvar 
10
>>> bar(6)
18

回答 4

我正在尝试使用该文件中的变量和设置,而不是调用一个单独的进程。

好了,只需使用import filename(减.py,需要在同一目录中或上PYTHONPATH)导入文件即可运行该文件,使其变量,函数,类等在filename.variable命名空间中可用。

因此,如果您有cheddar.py垃圾邮件变量和函数鸡蛋,则可以使用导入它们import cheddar,通过来访问变量cheddar.spam并通过调用来运行函数cheddar.eggs()

如果您的代码位于cheddar.py函数之外,那么它将立即运行,但是构建在导入时运行内容的应用程序将使重用代码变得困难。如果可能的话,请将所有内容放入函数或类中。

I’m trying to use variables and settings from that file, not to invoke a separate process.

Well, simply importing the file with import filename (minus .py, needs to be in the same directory or on your PYTHONPATH) will run the file, making its variables, functions, classes, etc. available in the filename.variable namespace.

So if you have cheddar.py with the variable spam and the function eggs – you can import them with import cheddar, access the variable with cheddar.spam and run the function by calling cheddar.eggs()

If you have code in cheddar.py that is outside a function, it will be run immediately, but building applications that runs stuff on import is going to make it hard to reuse your code. If a all possible, put everything inside functions or classes.


回答 5

我认为最好的方法是:

import yourfile

并在修改yourfile.py之后

reload(yourfile)   

要么

import imp; 
imp.reload(yourfile) in python3

但这会使函数和类看起来像:yourfile.function1,yourfile.class1 …..

如果您不能接受,那么最终的解决方案是:

reload(yourfile)
from yourfile import *

From my view, the best way is:

import yourfile

and after modifying yourfile.py

reload(yourfile)   

or

import imp; 
imp.reload(yourfile) in python3

but this will make the function and classes looks like that: yourfile.function1, yourfile.class1…..

If you cannot accept those, the finally solution is:

reload(yourfile)
from yourfile import *

回答 6

我不是专家,但这是我注意到的:

例如,如果您的代码是mycode.py,而您只键入“ import mycode”,则Python将执行该代码,但不会使您的所有变量都可用于解释器。我发现如果要将所有变量提供给解释器,则应键入“ from mycode import *”。

I am not an expert but this is what I noticed:

if your code is mycode.py for instance, and you type just ‘import mycode’, Python will execute it but it will not make all your variables available to the interpreter. I found that you should type actually ‘from mycode import *’ if you want to make all variables available to the interpreter.


回答 7

做就是了,

from my_file import *

确保不添加.py扩展名。如果您使用子目录中的.py文件,

from my_dir.my_file import *

Just do,

from my_file import *

Make sure not to add .py extension. If your .py file in subdirectory use,

from my_dir.my_file import *

回答 8

对于python3使用或者与xxxx = nameyourfile

exec(open('./xxxx.py').read())

For python3 use either with xxxx = name of yourfile.

exec(open('./xxxx.py').read())

回答 9

对于Python 3:

>>> exec(open("helloworld.py").read())

运行命令之前,请确保您位于正确的目录中。

要从其他目录运行文件,可以使用以下命令:

with open ("C:\\Users\\UserName\\SomeFolder\\helloworld.py", "r") as file:
    exec(file.read())

For Python 3:

>>> exec(open("helloworld.py").read())

Make sure that you’re in the correct directory before running the command.

To run a file from a different directory, you can use the below command:

with open ("C:\\Users\\UserName\\SomeFolder\\helloworld.py", "r") as file:
    exec(file.read())

回答 10

假设您需要以下功能:

  1. 源文件在调试器中的行为正常(文件名显示在堆栈中,等等)
  2. __name__ == '__main__' 为True,因此脚本可以像脚本一样正常运行。

exec(open('foo.py').read())故障特征1的import foo策略失败,功能2

要获得两者,您需要这样做:

    source = open(filename).read()
    code = compile(source, filename, 'exec')
    exec(code)

Supposing you desire the following features:

  1. Source file behaves properly in your debugger (filename shows in stack, etc)
  2. __name__ == '__main__' is True so scripts behave properly as scripts.

The exec(open('foo.py').read()) fails feature 1 The import foo strategy fails feature 2

To get both, you need this:

    source = open(filename).read()
    code = compile(source, filename, 'exec')
    exec(code)

等效的熊猫数(不同)

问题:等效的熊猫数(不同)

我使用pandas作为数据库替代品,因为我有多个数据库(oracle,mssql等),并且无法对SQL等效命令进行一系列命令。

我在DataFrame中加载了一个带有一些列的表:

YEARMONTH, CLIENTCODE, SIZE, .... etc etc

在SQL中,每年计算不同客户端的数量将是:

SELECT count(distinct CLIENTCODE) FROM table GROUP BY YEARMONTH;

结果将是

201301    5000
201302    13245

如何在熊猫中做到这一点?

I am using pandas as a db substitute as I have multiple databases (oracle, mssql, etc) and I am unable to make a sequence of commands to a SQL equivalent.

I have a table loaded in a DataFrame with some columns:

YEARMONTH, CLIENTCODE, SIZE, .... etc etc

In SQL, to count the amount of different clients per year would be:

SELECT count(distinct CLIENTCODE) FROM table GROUP BY YEARMONTH;

And the result would be

201301    5000
201302    13245

How can I do that in pandas?


回答 0

我相信这就是您想要的:

table.groupby('YEARMONTH').CLIENTCODE.nunique()

例:

In [2]: table
Out[2]: 
   CLIENTCODE  YEARMONTH
0           1     201301
1           1     201301
2           2     201301
3           1     201302
4           2     201302
5           2     201302
6           3     201302

In [3]: table.groupby('YEARMONTH').CLIENTCODE.nunique()
Out[3]: 
YEARMONTH
201301       2
201302       3

I believe this is what you want:

table.groupby('YEARMONTH').CLIENTCODE.nunique()

Example:

In [2]: table
Out[2]: 
   CLIENTCODE  YEARMONTH
0           1     201301
1           1     201301
2           2     201301
3           1     201302
4           2     201302
5           2     201302
6           3     201302

In [3]: table.groupby('YEARMONTH').CLIENTCODE.nunique()
Out[3]: 
YEARMONTH
201301       2
201302       3

回答 1

这是另一种方法,非常简单,可以说您的数据框名称为daat,列名称为YEARMONTH

daat.YEARMONTH.value_counts()

Here is another method, much simple, lets say your dataframe name is daat and column name is YEARMONTH

daat.YEARMONTH.value_counts()

回答 2

有趣的是,通常len(unique())速度比快几倍(3x-15x)nunique()

Interestingly enough, very often len(unique()) is a few times (3x-15x) faster than nunique().


回答 3

使用crosstab,这将返回比groupby nunique

pd.crosstab(df.YEARMONTH,df.CLIENTCODE)
Out[196]: 
CLIENTCODE  1  2  3
YEARMONTH          
201301      2  1  0
201302      1  2  1

稍作修改后,产生结果

pd.crosstab(df.YEARMONTH,df.CLIENTCODE).ne(0).sum(1)
Out[197]: 
YEARMONTH
201301    2
201302    3
dtype: int64

Using crosstab, this will return more information than groupby nunique

pd.crosstab(df.YEARMONTH,df.CLIENTCODE)
Out[196]: 
CLIENTCODE  1  2  3
YEARMONTH          
201301      2  1  0
201302      1  2  1

After a little bit modify ,yield the result

pd.crosstab(df.YEARMONTH,df.CLIENTCODE).ne(0).sum(1)
Out[197]: 
YEARMONTH
201301    2
201302    3
dtype: int64

回答 4

我也在使用,nunique但是如果您必须使用诸如'min', 'max', 'count' or 'mean'etc之类的聚合函数,它将非常有帮助。

df.groupby('YEARMONTH')['CLIENTCODE'].transform('nunique') #count(distinct)
df.groupby('YEARMONTH')['CLIENTCODE'].transform('min')     #min
df.groupby('YEARMONTH')['CLIENTCODE'].transform('max')     #max
df.groupby('YEARMONTH')['CLIENTCODE'].transform('mean')    #average
df.groupby('YEARMONTH')['CLIENTCODE'].transform('count')   #count

I am also using nunique but it will be very helpful if you have to use an aggregate function like 'min', 'max', 'count' or 'mean' etc.

df.groupby('YEARMONTH')['CLIENTCODE'].transform('nunique') #count(distinct)
df.groupby('YEARMONTH')['CLIENTCODE'].transform('min')     #min
df.groupby('YEARMONTH')['CLIENTCODE'].transform('max')     #max
df.groupby('YEARMONTH')['CLIENTCODE'].transform('mean')    #average
df.groupby('YEARMONTH')['CLIENTCODE'].transform('count')   #count

回答 5

使用新的Pandas版本,很容易获得数据框

unique_count = pd.groupby(['YEARMONTH'], as_index=False).agg(uniq_CLIENTCODE =('CLIENTCODE',pd.Series.count))

With new pandas version, it is easy to get as dataframe

unique_count = pd.groupby(['YEARMONTH'], as_index=False).agg(uniq_CLIENTCODE =('CLIENTCODE',pd.Series.count))

回答 6

这是一种在多个列上具有不同计数的方法。让我们来一些数据:

data = {'CLIENT_CODE':[1,1,2,1,2,2,3],
        'YEAR_MONTH':[201301,201301,201301,201302,201302,201302,201302],
        'PRODUCT_CODE': [100,150,220,400,50,80,100]
       }
table = pd.DataFrame(data)
table

CLIENT_CODE YEAR_MONTH  PRODUCT_CODE
0   1       201301      100
1   1       201301      150
2   2       201301      220
3   1       201302      400
4   2       201302      50
5   2       201302      80
6   3       201302      100

现在,列出感兴趣的列,并使用经过稍微修改的语法的groupby:

columns = ['YEAR_MONTH', 'PRODUCT_CODE']
table[columns].groupby(table['CLIENT_CODE']).nunique()

我们获得:

YEAR_MONTH  PRODUCT_CODE CLIENT_CODE        
1           2            3
2           2            3
3           1            1

Here an approach to have count distinct over multiple columns. Let’s have some data:

data = {'CLIENT_CODE':[1,1,2,1,2,2,3],
        'YEAR_MONTH':[201301,201301,201301,201302,201302,201302,201302],
        'PRODUCT_CODE': [100,150,220,400,50,80,100]
       }
table = pd.DataFrame(data)
table

CLIENT_CODE YEAR_MONTH  PRODUCT_CODE
0   1       201301      100
1   1       201301      150
2   2       201301      220
3   1       201302      400
4   2       201302      50
5   2       201302      80
6   3       201302      100

Now, list the columns of interest and use groupby in a slightly modified syntax:

columns = ['YEAR_MONTH', 'PRODUCT_CODE']
table[columns].groupby(table['CLIENT_CODE']).nunique()

We obtain:

YEAR_MONTH  PRODUCT_CODE CLIENT_CODE        
1           2            3
2           2            3
3           1            1

回答 7

列的不同以及其他列上的聚合

要获取任何列(CLIENTCODE在您的情况下)的不同数量的值,可以使用nunique。我们可以将输入作为字典传递给agg函数,以及其他列的聚合:

grp_df = df.groupby('YEARMONTH').agg({'CLIENTCODE': ['nunique'],
                                      'other_col_1': ['sum', 'count']})

# to flatten the multi-level columns
grp_df.columns = ["_".join(col).strip() for col in grp_df.columns.values]

# if you wish to reset the index
grp_df.reset_index(inplace=True)

Distinct of column along with aggregations on other columns

To get the distinct number of values for any column (CLIENTCODE in your case), we can use nunique. We can pass the input as a dictionary in agg function, along with aggregations on other columns:

grp_df = df.groupby('YEARMONTH').agg({'CLIENTCODE': ['nunique'],
                                      'other_col_1': ['sum', 'count']})

# to flatten the multi-level columns
grp_df.columns = ["_".join(col).strip() for col in grp_df.columns.values]

# if you wish to reset the index
grp_df.reset_index(inplace=True)

用twinx()辅助轴:如何添加到图例?

问题:用twinx()辅助轴:如何添加到图例?

我有一个使用两个y轴的图twinx()。我还给行加了标签,并想用显示legend(),但我仅成功获得了图例中一个轴的标签:

import numpy as np
import matplotlib.pyplot as plt
from matplotlib import rc
rc('mathtext', default='regular')

fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(time, Swdown, '-', label = 'Swdown')
ax.plot(time, Rn, '-', label = 'Rn')
ax2 = ax.twinx()
ax2.plot(time, temp, '-r', label = 'temp')
ax.legend(loc=0)
ax.grid()
ax.set_xlabel("Time (h)")
ax.set_ylabel(r"Radiation ($MJ\,m^{-2}\,d^{-1}$)")
ax2.set_ylabel(r"Temperature ($^\circ$C)")
ax2.set_ylim(0, 35)
ax.set_ylim(-20,100)
plt.show()

因此,我仅获得图例中第一个轴的标签,而没有得到第二个轴的标签“ temp”。如何将第三个标签添加到图例?

在此处输入图片说明

I have a plot with two y-axes, using twinx(). I also give labels to the lines, and want to show them with legend(), but I only succeed to get the labels of one axis in the legend:

import numpy as np
import matplotlib.pyplot as plt
from matplotlib import rc
rc('mathtext', default='regular')

fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(time, Swdown, '-', label = 'Swdown')
ax.plot(time, Rn, '-', label = 'Rn')
ax2 = ax.twinx()
ax2.plot(time, temp, '-r', label = 'temp')
ax.legend(loc=0)
ax.grid()
ax.set_xlabel("Time (h)")
ax.set_ylabel(r"Radiation ($MJ\,m^{-2}\,d^{-1}$)")
ax2.set_ylabel(r"Temperature ($^\circ$C)")
ax2.set_ylim(0, 35)
ax.set_ylim(-20,100)
plt.show()

So I only get the labels of the first axis in the legend, and not the label ‘temp’ of the second axis. How could I add this third label to the legend?

enter image description here


回答 0

您可以通过添加以下行轻松添加第二个图例:

ax2.legend(loc=0)

您将获得:

在此处输入图片说明

但是,如果要将所有标签都放在一个图例上,则应执行以下操作:

import numpy as np
import matplotlib.pyplot as plt
from matplotlib import rc
rc('mathtext', default='regular')

time = np.arange(10)
temp = np.random.random(10)*30
Swdown = np.random.random(10)*100-10
Rn = np.random.random(10)*100-10

fig = plt.figure()
ax = fig.add_subplot(111)

lns1 = ax.plot(time, Swdown, '-', label = 'Swdown')
lns2 = ax.plot(time, Rn, '-', label = 'Rn')
ax2 = ax.twinx()
lns3 = ax2.plot(time, temp, '-r', label = 'temp')

# added these three lines
lns = lns1+lns2+lns3
labs = [l.get_label() for l in lns]
ax.legend(lns, labs, loc=0)

ax.grid()
ax.set_xlabel("Time (h)")
ax.set_ylabel(r"Radiation ($MJ\,m^{-2}\,d^{-1}$)")
ax2.set_ylabel(r"Temperature ($^\circ$C)")
ax2.set_ylim(0, 35)
ax.set_ylim(-20,100)
plt.show()

这会给你这个:

在此处输入图片说明

You can easily add a second legend by adding the line:

ax2.legend(loc=0)

You’ll get this:

enter image description here

But if you want all labels on one legend then you should do something like this:

import numpy as np
import matplotlib.pyplot as plt
from matplotlib import rc
rc('mathtext', default='regular')

time = np.arange(10)
temp = np.random.random(10)*30
Swdown = np.random.random(10)*100-10
Rn = np.random.random(10)*100-10

fig = plt.figure()
ax = fig.add_subplot(111)

lns1 = ax.plot(time, Swdown, '-', label = 'Swdown')
lns2 = ax.plot(time, Rn, '-', label = 'Rn')
ax2 = ax.twinx()
lns3 = ax2.plot(time, temp, '-r', label = 'temp')

# added these three lines
lns = lns1+lns2+lns3
labs = [l.get_label() for l in lns]
ax.legend(lns, labs, loc=0)

ax.grid()
ax.set_xlabel("Time (h)")
ax.set_ylabel(r"Radiation ($MJ\,m^{-2}\,d^{-1}$)")
ax2.set_ylabel(r"Temperature ($^\circ$C)")
ax2.set_ylim(0, 35)
ax.set_ylim(-20,100)
plt.show()

Which will give you this:

enter image description here


回答 1

我不确定此功能是否是新功能,但您也可以使用get_legend_handles_labels()方法,而不是自己跟踪行和标签:

import numpy as np
import matplotlib.pyplot as plt
from matplotlib import rc
rc('mathtext', default='regular')

pi = np.pi

# fake data
time = np.linspace (0, 25, 50)
temp = 50 / np.sqrt (2 * pi * 3**2) \
        * np.exp (-((time - 13)**2 / (3**2))**2) + 15
Swdown = 400 / np.sqrt (2 * pi * 3**2) * np.exp (-((time - 13)**2 / (3**2))**2)
Rn = Swdown - 10

fig = plt.figure()
ax = fig.add_subplot(111)

ax.plot(time, Swdown, '-', label = 'Swdown')
ax.plot(time, Rn, '-', label = 'Rn')
ax2 = ax.twinx()
ax2.plot(time, temp, '-r', label = 'temp')

# ask matplotlib for the plotted objects and their labels
lines, labels = ax.get_legend_handles_labels()
lines2, labels2 = ax2.get_legend_handles_labels()
ax2.legend(lines + lines2, labels + labels2, loc=0)

ax.grid()
ax.set_xlabel("Time (h)")
ax.set_ylabel(r"Radiation ($MJ\,m^{-2}\,d^{-1}$)")
ax2.set_ylabel(r"Temperature ($^\circ$C)")
ax2.set_ylim(0, 35)
ax.set_ylim(-20,100)
plt.show()

I’m not sure if this functionality is new, but you can also use the get_legend_handles_labels() method rather than keeping track of lines and labels yourself:

import numpy as np
import matplotlib.pyplot as plt
from matplotlib import rc
rc('mathtext', default='regular')

pi = np.pi

# fake data
time = np.linspace (0, 25, 50)
temp = 50 / np.sqrt (2 * pi * 3**2) \
        * np.exp (-((time - 13)**2 / (3**2))**2) + 15
Swdown = 400 / np.sqrt (2 * pi * 3**2) * np.exp (-((time - 13)**2 / (3**2))**2)
Rn = Swdown - 10

fig = plt.figure()
ax = fig.add_subplot(111)

ax.plot(time, Swdown, '-', label = 'Swdown')
ax.plot(time, Rn, '-', label = 'Rn')
ax2 = ax.twinx()
ax2.plot(time, temp, '-r', label = 'temp')

# ask matplotlib for the plotted objects and their labels
lines, labels = ax.get_legend_handles_labels()
lines2, labels2 = ax2.get_legend_handles_labels()
ax2.legend(lines + lines2, labels + labels2, loc=0)

ax.grid()
ax.set_xlabel("Time (h)")
ax.set_ylabel(r"Radiation ($MJ\,m^{-2}\,d^{-1}$)")
ax2.set_ylabel(r"Temperature ($^\circ$C)")
ax2.set_ylim(0, 35)
ax.set_ylim(-20,100)
plt.show()

回答 2

从matplotlib 2.1版开始,您可以使用图例。可以创建一个图例ax.legend(),而不是通过轴的手柄ax生成图例。

fig.legend(loc =“右上”)

它将收集图中所有子图的所有手柄。由于它是一个人物图例,因此它将放置在人物的角上,并且loc参数是相对于人物的。

import numpy as np
import matplotlib.pyplot as plt

x = np.linspace(0,10)
y = np.linspace(0,10)
z = np.sin(x/3)**2*98

fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(x,y, '-', label = 'Quantity 1')

ax2 = ax.twinx()
ax2.plot(x,z, '-r', label = 'Quantity 2')
fig.legend(loc="upper right")

ax.set_xlabel("x [units]")
ax.set_ylabel(r"Quantity 1")
ax2.set_ylabel(r"Quantity 2")

plt.show()

在此处输入图片说明

为了将图例放回轴中,可以提供a bbox_to_anchor和a bbox_transform。后者是图例应驻留的轴的轴变换。前者可以是loc轴坐标中给定定义的边的坐标。

fig.legend(loc="upper right", bbox_to_anchor=(1,1), bbox_transform=ax.transAxes)

在此处输入图片说明

From matplotlib version 2.1 onwards, you may use a figure legend. Instead of ax.legend(), which produces a legend with the handles from the axes ax, one can create a figure legend

fig.legend(loc="upper right")

which will gather all handles from all subplots in the figure. Since it is a figure legend, it will be placed at the corner of the figure, and the loc argument is relative to the figure.

import numpy as np
import matplotlib.pyplot as plt

x = np.linspace(0,10)
y = np.linspace(0,10)
z = np.sin(x/3)**2*98

fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(x,y, '-', label = 'Quantity 1')

ax2 = ax.twinx()
ax2.plot(x,z, '-r', label = 'Quantity 2')
fig.legend(loc="upper right")

ax.set_xlabel("x [units]")
ax.set_ylabel(r"Quantity 1")
ax2.set_ylabel(r"Quantity 2")

plt.show()

enter image description here

In order to place the legend back into the axes, one would supply a bbox_to_anchor and a bbox_transform. The latter would be the axes transform of the axes the legend should reside in. The former may be the coordinates of the edge defined by loc given in axes coordinates.

fig.legend(loc="upper right", bbox_to_anchor=(1,1), bbox_transform=ax.transAxes)

enter image description here


回答 3

您可以通过在ax中添加行来轻松获得所需的内容:

ax.plot([], [], '-r', label = 'temp')

要么

ax.plot(np.nan, '-r', label = 'temp')

除了给ax图例添加标签之外,这什么都不会绘制。

我认为这是一种简单得多的方法。当第二轴上只有几条线时,无需自动跟踪线,因为像上面这样的手工固定将非常容易。无论如何,这取决于您的需求。

整个代码如下:

import numpy as np
import matplotlib.pyplot as plt
from matplotlib import rc
rc('mathtext', default='regular')

time = np.arange(22.)
temp = 20*np.random.rand(22)
Swdown = 10*np.random.randn(22)+40
Rn = 40*np.random.rand(22)

fig = plt.figure()
ax = fig.add_subplot(111)
ax2 = ax.twinx()

#---------- look at below -----------

ax.plot(time, Swdown, '-', label = 'Swdown')
ax.plot(time, Rn, '-', label = 'Rn')

ax2.plot(time, temp, '-r')  # The true line in ax2
ax.plot(np.nan, '-r', label = 'temp')  # Make an agent in ax

ax.legend(loc=0)

#---------------done-----------------

ax.grid()
ax.set_xlabel("Time (h)")
ax.set_ylabel(r"Radiation ($MJ\,m^{-2}\,d^{-1}$)")
ax2.set_ylabel(r"Temperature ($^\circ$C)")
ax2.set_ylim(0, 35)
ax.set_ylim(-20,100)
plt.show()

情节如下:

在此处输入图片说明


更新:添加更好的版本:

ax.plot(np.nan, '-r', label = 'temp')

plot(0, 0)可能会改变轴范围,但无济于事。


散布的另一个示例

ax.scatter([], [], s=100, label = 'temp')  # Make an agent in ax
ax2.scatter(time, temp, s=10)  # The true scatter in ax2

ax.legend(loc=1, framealpha=1)

You can easily get what you want by adding the line in ax:

ax.plot([], [], '-r', label = 'temp')

or

ax.plot(np.nan, '-r', label = 'temp')

This would plot nothing but add a label to legend of ax.

I think this is a much easier way. It’s not necessary to track lines automatically when you have only a few lines in the second axes, as fixing by hand like above would be quite easy. Anyway, it depends on what you need.

The whole code is as below:

import numpy as np
import matplotlib.pyplot as plt
from matplotlib import rc
rc('mathtext', default='regular')

time = np.arange(22.)
temp = 20*np.random.rand(22)
Swdown = 10*np.random.randn(22)+40
Rn = 40*np.random.rand(22)

fig = plt.figure()
ax = fig.add_subplot(111)
ax2 = ax.twinx()

#---------- look at below -----------

ax.plot(time, Swdown, '-', label = 'Swdown')
ax.plot(time, Rn, '-', label = 'Rn')

ax2.plot(time, temp, '-r')  # The true line in ax2
ax.plot(np.nan, '-r', label = 'temp')  # Make an agent in ax

ax.legend(loc=0)

#---------------done-----------------

ax.grid()
ax.set_xlabel("Time (h)")
ax.set_ylabel(r"Radiation ($MJ\,m^{-2}\,d^{-1}$)")
ax2.set_ylabel(r"Temperature ($^\circ$C)")
ax2.set_ylim(0, 35)
ax.set_ylim(-20,100)
plt.show()

The plot is as below:

enter image description here


Update: add a better version:

ax.plot(np.nan, '-r', label = 'temp')

This will do nothing while plot(0, 0) may change the axis range.


One extra example for scatter

ax.scatter([], [], s=100, label = 'temp')  # Make an agent in ax
ax2.scatter(time, temp, s=10)  # The true scatter in ax2

ax.legend(loc=1, framealpha=1)

回答 4

可能适合您需求的快速技巧。

取下盒子的框架,然后手动将两个图例彼此相邻放置。像这样

ax1.legend(loc = (.75,.1), frameon = False)
ax2.legend( loc = (.75, .05), frameon = False)

位置元组从左到右和从下到上的百分比代表图表中的位置。

A quick hack that may suit your needs..

Take off the frame of the box and manually position the two legends next to each other. Something like this..

ax1.legend(loc = (.75,.1), frameon = False)
ax2.legend( loc = (.75, .05), frameon = False)

Where the loc tuple is left-to-right and bottom-to-top percentages that represent the location in the chart.


回答 5

我找到了以下官方matplotlib示例,该示例使用host_subplot在一个图例中显示多个y轴和所有不同的标签。无需任何解决方法。到目前为止,我找到的最佳解决方案。 http://matplotlib.org/examples/axes_grid/demo_parasite_axes2.html

from mpl_toolkits.axes_grid1 import host_subplot
import mpl_toolkits.axisartist as AA
import matplotlib.pyplot as plt

host = host_subplot(111, axes_class=AA.Axes)
plt.subplots_adjust(right=0.75)

par1 = host.twinx()
par2 = host.twinx()

offset = 60
new_fixed_axis = par2.get_grid_helper().new_fixed_axis
par2.axis["right"] = new_fixed_axis(loc="right",
                                    axes=par2,
                                    offset=(offset, 0))

par2.axis["right"].toggle(all=True)

host.set_xlim(0, 2)
host.set_ylim(0, 2)

host.set_xlabel("Distance")
host.set_ylabel("Density")
par1.set_ylabel("Temperature")
par2.set_ylabel("Velocity")

p1, = host.plot([0, 1, 2], [0, 1, 2], label="Density")
p2, = par1.plot([0, 1, 2], [0, 3, 2], label="Temperature")
p3, = par2.plot([0, 1, 2], [50, 30, 15], label="Velocity")

par1.set_ylim(0, 4)
par2.set_ylim(1, 65)

host.legend()

plt.draw()
plt.show()

I found an following official matplotlib example that uses host_subplot to display multiple y-axes and all the different labels in one legend. No workaround necessary. Best solution I found so far. http://matplotlib.org/examples/axes_grid/demo_parasite_axes2.html

from mpl_toolkits.axes_grid1 import host_subplot
import mpl_toolkits.axisartist as AA
import matplotlib.pyplot as plt

host = host_subplot(111, axes_class=AA.Axes)
plt.subplots_adjust(right=0.75)

par1 = host.twinx()
par2 = host.twinx()

offset = 60
new_fixed_axis = par2.get_grid_helper().new_fixed_axis
par2.axis["right"] = new_fixed_axis(loc="right",
                                    axes=par2,
                                    offset=(offset, 0))

par2.axis["right"].toggle(all=True)

host.set_xlim(0, 2)
host.set_ylim(0, 2)

host.set_xlabel("Distance")
host.set_ylabel("Density")
par1.set_ylabel("Temperature")
par2.set_ylabel("Velocity")

p1, = host.plot([0, 1, 2], [0, 1, 2], label="Density")
p2, = par1.plot([0, 1, 2], [0, 3, 2], label="Temperature")
p3, = par2.plot([0, 1, 2], [50, 30, 15], label="Velocity")

par1.set_ylim(0, 4)
par2.set_ylim(1, 65)

host.legend()

plt.draw()
plt.show()

如何将time.struct_time对象转换为datetime对象?

问题:如何将time.struct_time对象转换为datetime对象?

如何将Python time.struct_time对象转换为datetime.datetime对象?

我有一个提供第一个库的图书馆,一个提供第二个库的图书馆。

How do you convert a Python time.struct_time object into a datetime.datetime object?

I have a library that provides the first one and a second library that wants the second one.


回答 0

使用time.mktime()将时间元组(以本地时间表示)转换为自大纪元以来的秒数,然后使用datetime.fromtimestamp()获得datetime对象。

from datetime import datetime
from time import mktime

dt = datetime.fromtimestamp(mktime(struct))

Use time.mktime() to convert the time tuple (in localtime) into seconds since the Epoch, then use datetime.fromtimestamp() to get the datetime object.

from datetime import datetime
from time import mktime

dt = datetime.fromtimestamp(mktime(struct))

回答 1

像这样:

>>> structTime = time.localtime()
>>> datetime.datetime(*structTime[:6])
datetime.datetime(2009, 11, 8, 20, 32, 35)

Like this:

>>> structTime = time.localtime()
>>> datetime.datetime(*structTime[:6])
datetime.datetime(2009, 11, 8, 20, 32, 35)

回答 2

这不是您问题的直接答案(已经很好回答了)。但是,由于有几次时间在我的基础上咬了我一口,所以我不能过分强调,您应该仔细查看一下time.struct_time对象提供的内容,而不是其他时间字段可能提供的内容。

假设您同时拥有一个time.struct_time对象和其他一些日期/时间字符串,则将两者进行比较,并确保您不会丢失数据并无意中创建了一个简单的datetime对象,否则您可以这样做。

例如,出色的feedparser模块将返回一个“ published”字段,并可能在其“ published_pa​​rsed”字段中返回一个time.struct_time对象:

time.struct_time(tm_year=2013, tm_mon=9, tm_mday=9, tm_hour=23, tm_min=57, tm_sec=42, tm_wday=0, tm_yday=252, tm_isdst=0)

现在,请注意您在“已发布”字段中实际得到的内容。

Mon, 09 Sep 2013 19:57:42 -0400

斯托曼的胡子!时区信息!

在这种情况下,懒惰的人可能想要使用出色的dateutil模块来保留时区信息:

from dateutil import parser
dt = parser.parse(entry["published"])
print "published", entry["published"])
print "dt", dt
print "utcoffset", dt.utcoffset()
print "tzinfo", dt.tzinfo
print "dst", dt.dst()

这给了我们:

published Mon, 09 Sep 2013 19:57:42 -0400
dt 2013-09-09 19:57:42-04:00
utcoffset -1 day, 20:00:00
tzinfo tzoffset(None, -14400)
dst 0:00:00

然后,可以使用可识别时区的datetime对象将所有时间标准化为UTC或任何您认为很棒的东西。

This is not a direct answer to your question (which was answered pretty well already). However, having had times bite me on the fundament several times, I cannot stress enough that it would behoove you to look closely at what your time.struct_time object is providing, vs. what other time fields may have.

Assuming you have both a time.struct_time object, and some other date/time string, compare the two, and be sure you are not losing data and inadvertently creating a naive datetime object, when you can do otherwise.

For example, the excellent feedparser module will return a “published” field and may return a time.struct_time object in its “published_parsed” field:

time.struct_time(tm_year=2013, tm_mon=9, tm_mday=9, tm_hour=23, tm_min=57, tm_sec=42, tm_wday=0, tm_yday=252, tm_isdst=0)

Now note what you actually get with the “published” field.

Mon, 09 Sep 2013 19:57:42 -0400

By Stallman‘s Beard! Timezone information!

In this case, the lazy man might want to use the excellent dateutil module to keep the timezone information:

from dateutil import parser
dt = parser.parse(entry["published"])
print "published", entry["published"])
print "dt", dt
print "utcoffset", dt.utcoffset()
print "tzinfo", dt.tzinfo
print "dst", dt.dst()

which gives us:

published Mon, 09 Sep 2013 19:57:42 -0400
dt 2013-09-09 19:57:42-04:00
utcoffset -1 day, 20:00:00
tzinfo tzoffset(None, -14400)
dst 0:00:00

One could then use the timezone-aware datetime object to normalize all time to UTC or whatever you think is awesome.


是否只能在Python中声明变量而不分配任何值?

问题:是否只能在Python中声明变量而不分配任何值?

是否可以像这样在Python中声明变量?

var

以便将其初始化为None?似乎Python允许这样做,但是一旦您访问它,它就会崩溃。这可能吗?如果没有,为什么?

编辑:我想这样做的情况下:

value

for index in sequence:

   if value == None and conditionMet:
       value = index
       break

重复

有关

Is it possible to declare a variable in Python, like so?:

var

so that it initialized to None? It seems like Python allows this, but as soon as you access it, it crashes. Is this possible? If not, why?

EDIT: I want to do this for cases like this:

value

for index in sequence:

   if value == None and conditionMet:
       value = index
       break

Duplicate

Related


回答 0

为什么不这样做:

var = None

Python是动态的,因此您无需声明任何东西。它们自动存在于分配它们的第一个范围中。因此,您只需要一个如上所述的常规旧赋值语句即可。

很好,因为您永远不会以未初始化的变量结尾。但是要小心-这并不意味着您最终不会得到错误初始化的变量。如果您将初始化为None,请确保这是您真正想要的,并尽可能分配更有意义的内容。

Why not just do this:

var = None

Python is dynamic, so you don’t need to declare things; they exist automatically in the first scope where they’re assigned. So, all you need is a regular old assignment statement as above.

This is nice, because you’ll never end up with an uninitialized variable. But be careful — this doesn’t mean that you won’t end up with incorrectly initialized variables. If you init something to None, make sure that’s what you really want, and assign something more meaningful if you can.


回答 1

我衷心建议您阅读其他语言的“变量”(我将其添加为相关链接)–在两分钟内,您将知道Python具有“名称”,而不是“变量”。

val = None
# ...
if val is None:
   val = any_object

I’d heartily recommend that you read Other languages have “variables” (I added it as a related link) – in two minutes you’ll know that Python has “names”, not “variables”.

val = None
# ...
if val is None:
   val = any_object

回答 2

在Python 3.6+中,您可以为此使用变量注释:

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

PEP 484引入了类型提示,也就是类型注释。尽管它的主要焦点是函数注释,但它也引入了类型注释的概念来注释变量:

# 'captain' is a string (Note: initial value is a problem)
captain = ...  # type: str

PEP 526旨在向Python添加语法来注释变量的类型(包括类变量和实例变量),而不是通过注释来表示它们:

captain: str  # Note: no initial value!

似乎更符合您的要求:“是否有可能只声明变量而不在Python中分配任何值?”

In Python 3.6+ you could use Variable Annotations for this:

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

PEP 484 introduced type hints, a.k.a. type annotations. While its main focus was function annotations, it also introduced the notion of type comments to annotate variables:

# 'captain' is a string (Note: initial value is a problem)
captain = ...  # type: str

PEP 526 aims at adding syntax to Python for annotating the types of variables (including class variables and instance variables), instead of expressing them through comments:

captain: str  # Note: no initial value!

It seems to be more directly in line with what you were asking “Is it possible only to declare a variable without assigning any value in Python?”


回答 3

我不确定您要做什么。Python是一种非常动态的语言。在实际分配或使用变量之前,通常不需要声明变量。我想你要做的就是

foo = None

它将把值None赋给变量foo

编辑:您似乎真正想做的就是:

#note how I don't do *anything* with value here
#we can just start using it right inside the loop

for index in sequence:
   if conditionMet:
       value = index
       break

try:
    doSomething(value)
except NameError:
    print "Didn't find anything"

从如此简短的代码示例中很难分辨出这是否是正确的样式,但这一种更加“ Pythonic”的工作方式。

编辑:以下是JFS的注释(发布在此处以显示代码)

与OP的问题无关,但以上代码可以重写为:

for item in sequence:
    if some_condition(item): 
       found = True
       break
else: # no break or len(sequence) == 0
    found = False

if found:
   do_something(item)

注意:如果some_condition()引发异常,则found没有约束。
注意:如果len(sequence)== 0,则item表示未绑定。

上面的代码不建议使用。其目的是说明局部变量的工作方式,即在这种情况下只能在运行时确定“变量”是否为“定义”。首选方式:

for item in sequence:
    if some_condition(item):
       do_something(item)
       break

要么

found = False
for item in sequence:
    if some_condition(item):
       found = True
       break

if found:
   do_something(item)

I’m not sure what you’re trying to do. Python is a very dynamic language; you don’t usually need to declare variables until you’re actually going to assign to or use them. I think what you want to do is just

foo = None

which will assign the value None to the variable foo.

EDIT: What you really seem to want to do is just this:

#note how I don't do *anything* with value here
#we can just start using it right inside the loop

for index in sequence:
   if conditionMet:
       value = index
       break

try:
    doSomething(value)
except NameError:
    print "Didn't find anything"

It’s a little difficult to tell if that’s really the right style to use from such a short code example, but it is a more “Pythonic” way to work.

EDIT: below is comment by JFS (posted here to show the code)

Unrelated to the OP’s question but the above code can be rewritten as:

for item in sequence:
    if some_condition(item): 
       found = True
       break
else: # no break or len(sequence) == 0
    found = False

if found:
   do_something(item)

NOTE: if some_condition() raises an exception then found is unbound.
NOTE: if len(sequence) == 0 then item is unbound.

The above code is not advisable. Its purpose is to illustrate how local variables work, namely whether “variable” is “defined” could be determined only at runtime in this case. Preferable way:

for item in sequence:
    if some_condition(item):
       do_something(item)
       break

Or

found = False
for item in sequence:
    if some_condition(item):
       found = True
       break

if found:
   do_something(item)

回答 4

我通常将变量初始化为某种表示类型的东西

var = ""

要么

var = 0

如果它将成为对象,那么在实例化它之前不要对其进行初始化:

var = Var()

I usually initialize the variable to something that denotes the type like

var = ""

or

var = 0

If it is going to be an object then don’t initialize it until you instantiate it:

var = Var()

回答 5

好吧,如果您想检查是否定义了变量,那为什么不检查它是否在locals()或globals()数组中呢?您的代码被重写:

for index in sequence:
   if 'value' not in globals() and conditionMet:
       value = index
       break

如果它是您要查找的局部变量,则将locals()替换为globals()。

Well, if you want to check if a variable is defined or not then why not check if its in the locals() or globals() arrays? Your code rewritten:

for index in sequence:
   if 'value' not in globals() and conditionMet:
       value = index
       break

If it’s a local variable you are looking for then replace globals() with locals().


回答 6

首先,我对您最初提出的问题的回答

问:如何查找是否在代码中的某个点定义了变量?

答:在源文件中读取,直到看到定义该变量的行。

但是,此外,您还给出了一个代码示例,其中的各种排列都是pythonic的。您正在寻找一种扫描序列以查找与条件匹配的元素的方法,因此这里有一些解决方案:

def findFirstMatch(sequence):
    for value in sequence:
        if matchCondition(value):
            return value

    raise LookupError("Could not find match in sequence")

显然,在此示例中,您可以根据要实现的目标raise用替换return None

如果您想要满足条件的所有内容,则可以执行以下操作:

def findAllMatches(sequence):
    matches = []
    for value in sequence:
        if matchCondition(value):
            matches.append(value)

    return matches

还有另一种方法yield,我不会麻烦您看,因为它的工作方式非常复杂。

此外,有一种方法可以实现此目的:

all_matches = [value for value in sequence if matchCondition(value)]

First of all, my response to the question you’ve originally asked

Q: How do I discover if a variable is defined at a point in my code?

A: Read up in the source file until you see a line where that variable is defined.

But further, you’ve given a code example that there are various permutations of that are quite pythonic. You’re after a way to scan a sequence for elements that match a condition, so here are some solutions:

def findFirstMatch(sequence):
    for value in sequence:
        if matchCondition(value):
            return value

    raise LookupError("Could not find match in sequence")

Clearly in this example you could replace the raise with a return None depending on what you wanted to achieve.

If you wanted everything that matched the condition you could do this:

def findAllMatches(sequence):
    matches = []
    for value in sequence:
        if matchCondition(value):
            matches.append(value)

    return matches

There is another way of doing this with yield that I won’t bother showing you, because it’s quite complicated in the way that it works.

Further, there is a one line way of achieving this:

all_matches = [value for value in sequence if matchCondition(value)]

回答 7

如果我理解您的示例正确,那么无论如何您都无需在if语句中引用“值”。只要将其设置为任何值,您就可以摆脱循环。

value = None
for index in sequence:
   doSomethingHere
   if conditionMet:
       value = index
       break 

If I’m understanding your example right, you don’t need to refer to ‘value’ in the if statement anyway. You’re breaking out of the loop as soon as it could be set to anything.

value = None
for index in sequence:
   doSomethingHere
   if conditionMet:
       value = index
       break 

回答 8

您似乎正在尝试用Python编写C。如果您想按顺序查找某物,Python提供了内置函数来实现,例如

value = sequence.index(blarg)

You look like you’re trying to write C in Python. If you want to find something in a sequence, Python has builtin functions to do that, like

value = sequence.index(blarg)

回答 9

这是一个好问题,不幸的var = None是,答案很差,因为已经分配了一个值,并且如果您的脚本多次运行,则None每次都会覆盖它。

它与没有分配的定义不同。我仍在尝试找出如何绕过此问题的方法。

It is a good question and unfortunately bad answers as var = None is already assigning a value, and if your script runs multiple times it is overwritten with None every time.

It is not the same as defining without assignment. I am still trying to figure out how to bypass this issue.


回答 10

是否可以在Python中声明变量(var = None):

def decl_var(var=None):
if var is None:
    var = []
var.append(1)
return var

Is it possible to declare a variable in Python (var=None):

def decl_var(var=None):
if var is None:
    var = []
var.append(1)
return var

回答 11

var_str = str()
var_int = int()
var_str = str()
var_int = int()

回答 12

如果None是一个有效的数据值,那么您需要使用另一种方式来变量。您可以使用:

var = object()

尼克·科格兰Nick Coghlan)建议这个哨兵。

If None is a valid data value then you need to the variable another way. You could use:

var = object()

This sentinel is suggested by Nick Coghlan.


回答 13

您可以使用此丑陋的oneliner欺骗解释器。if None: var = None 除了将变量添加var到局部变量字典中而不进行初始化之外,它什么也没做。如果您随后尝试在函数中使用此变量,则解释器将引发UnboundLocalError异常。这也适用于非常古老的python版本。不简单,也不漂亮,但是不要对python有太多期望。

You can trick an interpreter with this ugly oneliner if None: var = None It do nothing else but adding a variable var to local variable dictionary, not initializing it. Interpreter will throw the UnboundLocalError exception if you try to use this variable in a function afterwards. This would works for very ancient python versions too. Not simple, nor beautiful, but don’t expect much from python.


如何漂亮地打印嵌套词典?

问题:如何漂亮地打印嵌套词典?

如何在Python中打印深度约为4的字典?我尝试使用进行漂亮的打印pprint(),但是没有用:

import pprint 
pp = pprint.PrettyPrinter(indent=4)
pp.pprint(mydict)

我只是想"\t"为每个嵌套添加一个缩进(),以便获得如下所示的内容:

key1
    value1
    value2
    key2
       value1
       value2

等等

我怎样才能做到这一点?

How can I pretty print a dictionary with depth of ~4 in Python? I tried pretty printing with pprint(), but it did not work:

import pprint 
pp = pprint.PrettyPrinter(indent=4)
pp.pprint(mydict)

I simply want an indentation ("\t") for each nesting, so that I get something like this:

key1
    value1
    value2
    key2
       value1
       value2

etc.

How can I do this?


回答 0

我不确定您希望格式看起来如何,但是可以从这样的函数开始:

def pretty(d, indent=0):
   for key, value in d.items():
      print('\t' * indent + str(key))
      if isinstance(value, dict):
         pretty(value, indent+1)
      else:
         print('\t' * (indent+1) + str(value))

I’m not sure how exactly you want the formatting to look like, but you could start with a function like this:

def pretty(d, indent=0):
   for key, value in d.items():
      print('\t' * indent + str(key))
      if isinstance(value, dict):
         pretty(value, indent+1)
      else:
         print('\t' * (indent+1) + str(value))

回答 1

我首先想到的是JSON序列化程序可能非常擅长嵌套字典,因此我会作弊并使用它:

>>> import json
>>> print json.dumps({'a':2, 'b':{'x':3, 'y':{'t1': 4, 't2':5}}},
...                  sort_keys=True, indent=4)
{
    "a": 2,
    "b": {
        "x": 3,
        "y": {
            "t1": 4,
            "t2": 5
        }
    }
}

My first thought was that the JSON serializer is probably pretty good at nested dictionaries, so I’d cheat and use that:

>>> import json
>>> print json.dumps({'a':2, 'b':{'x':3, 'y':{'t1': 4, 't2':5}}},
...                  sort_keys=True, indent=4)
{
    "a": 2,
    "b": {
        "x": 3,
        "y": {
            "t1": 4,
            "t2": 5
        }
    }
}

回答 2

您可以通过PyYAML尝试YAML。其输出可以微调。我建议从以下内容开始:

print yaml.dump(data, allow_unicode=True, default_flow_style=False)

结果非常可读;如果需要,也可以将其解析回Python。

编辑:

例:

>>> import yaml
>>> data = {'a':2, 'b':{'x':3, 'y':{'t1': 4, 't2':5}}}
>>> print yaml.dump(data, default_flow_style=False)
a: 2
b:
  x: 3
  y:
    t1: 4
    t2: 5

You could try YAML via PyYAML. Its output can be fine-tuned. I’d suggest starting with the following:

print yaml.dump(data, allow_unicode=True, default_flow_style=False)

The result is very readable; it can be also parsed back to Python if needed.

Edit:

Example:

>>> import yaml
>>> data = {'a':2, 'b':{'x':3, 'y':{'t1': 4, 't2':5}}}
>>> print yaml.dump(data, default_flow_style=False)
a: 2
b:
  x: 3
  y:
    t1: 4
    t2: 5

回答 3

到目前为止,我还没有看到任何漂亮的打印机至少以非常简单的格式模仿python解释器的输出,所以这是我的:

class Formatter(object):
    def __init__(self):
        self.types = {}
        self.htchar = '\t'
        self.lfchar = '\n'
        self.indent = 0
        self.set_formater(object, self.__class__.format_object)
        self.set_formater(dict, self.__class__.format_dict)
        self.set_formater(list, self.__class__.format_list)
        self.set_formater(tuple, self.__class__.format_tuple)

    def set_formater(self, obj, callback):
        self.types[obj] = callback

    def __call__(self, value, **args):
        for key in args:
            setattr(self, key, args[key])
        formater = self.types[type(value) if type(value) in self.types else object]
        return formater(self, value, self.indent)

    def format_object(self, value, indent):
        return repr(value)

    def format_dict(self, value, indent):
        items = [
            self.lfchar + self.htchar * (indent + 1) + repr(key) + ': ' +
            (self.types[type(value[key]) if type(value[key]) in self.types else object])(self, value[key], indent + 1)
            for key in value
        ]
        return '{%s}' % (','.join(items) + self.lfchar + self.htchar * indent)

    def format_list(self, value, indent):
        items = [
            self.lfchar + self.htchar * (indent + 1) + (self.types[type(item) if type(item) in self.types else object])(self, item, indent + 1)
            for item in value
        ]
        return '[%s]' % (','.join(items) + self.lfchar + self.htchar * indent)

    def format_tuple(self, value, indent):
        items = [
            self.lfchar + self.htchar * (indent + 1) + (self.types[type(item) if type(item) in self.types else object])(self, item, indent + 1)
            for item in value
        ]
        return '(%s)' % (','.join(items) + self.lfchar + self.htchar * indent)

要初始化它:

pretty = Formatter()

它可以支持为定义的类型添加格式器,您只需要为此创建一个函数,然后使用set_formater将其绑定到所需的类型即可:

from collections import OrderedDict

def format_ordereddict(self, value, indent):
    items = [
        self.lfchar + self.htchar * (indent + 1) +
        "(" + repr(key) + ', ' + (self.types[
            type(value[key]) if type(value[key]) in self.types else object
        ])(self, value[key], indent + 1) + ")"
        for key in value
    ]
    return 'OrderedDict([%s])' % (','.join(items) +
           self.lfchar + self.htchar * indent)
pretty.set_formater(OrderedDict, format_ordereddict)

出于历史原因,我保留了以前的漂亮打印机,它是一个函数而不是一个类,但是它们都可以以相同的方式使用,该类版本只允许更多功能:

def pretty(value, htchar='\t', lfchar='\n', indent=0):
    nlch = lfchar + htchar * (indent + 1)
    if type(value) is dict:
        items = [
            nlch + repr(key) + ': ' + pretty(value[key], htchar, lfchar, indent + 1)
            for key in value
        ]
        return '{%s}' % (','.join(items) + lfchar + htchar * indent)
    elif type(value) is list:
        items = [
            nlch + pretty(item, htchar, lfchar, indent + 1)
            for item in value
        ]
        return '[%s]' % (','.join(items) + lfchar + htchar * indent)
    elif type(value) is tuple:
        items = [
            nlch + pretty(item, htchar, lfchar, indent + 1)
            for item in value
        ]
        return '(%s)' % (','.join(items) + lfchar + htchar * indent)
    else:
        return repr(value)

要使用它:

>>> a = {'list':['a','b',1,2],'dict':{'a':1,2:'b'},'tuple':('a','b',1,2),'function':pretty,'unicode':u'\xa7',("tuple","key"):"valid"}
>>> a
{'function': <function pretty at 0x7fdf555809b0>, 'tuple': ('a', 'b', 1, 2), 'list': ['a', 'b', 1, 2], 'dict': {'a': 1, 2: 'b'}, 'unicode': u'\xa7', ('tuple', 'key'): 'valid'}
>>> print(pretty(a))
{
    'function': <function pretty at 0x7fdf555809b0>,
    'tuple': (
        'a',
        'b',
        1,
        2
    ),
    'list': [
        'a',
        'b',
        1,
        2
    ],
    'dict': {
        'a': 1,
        2: 'b'
    },
    'unicode': u'\xa7',
    ('tuple', 'key'): 'valid'
}

与其他版本相比:

  • 该解决方案直接寻找对象类型,因此您几乎可以打印几乎所有内容,而不仅是列表或字典。
  • 没有任何依赖性。
  • 一切都放在一个字符串中,因此您可以使用它进行任何操作。
  • 该类和函数已经过测试,可与Python 2.7和3.4一起使用。
  • 您可以在其中包含所有类型的对象,这是它们的表示形式,而不是放入结果中的它们的内容(因此,字符串带有引号,Unicode字符串可以完全表示…)。
  • 使用类版本,可以为所需的每种对象类型添加格式,也可以为已定义的对象类型更改格式。
  • 键可以是任何有效类型。
  • 缩进和换行符可以根据我们的需要进行更改。
  • 字典,列表和元组很漂亮。

As of what have been done, I don’t see any pretty printer that at least mimics the output of the python interpreter with very simple formatting so here’s mine :

class Formatter(object):
    def __init__(self):
        self.types = {}
        self.htchar = '\t'
        self.lfchar = '\n'
        self.indent = 0
        self.set_formater(object, self.__class__.format_object)
        self.set_formater(dict, self.__class__.format_dict)
        self.set_formater(list, self.__class__.format_list)
        self.set_formater(tuple, self.__class__.format_tuple)

    def set_formater(self, obj, callback):
        self.types[obj] = callback

    def __call__(self, value, **args):
        for key in args:
            setattr(self, key, args[key])
        formater = self.types[type(value) if type(value) in self.types else object]
        return formater(self, value, self.indent)

    def format_object(self, value, indent):
        return repr(value)

    def format_dict(self, value, indent):
        items = [
            self.lfchar + self.htchar * (indent + 1) + repr(key) + ': ' +
            (self.types[type(value[key]) if type(value[key]) in self.types else object])(self, value[key], indent + 1)
            for key in value
        ]
        return '{%s}' % (','.join(items) + self.lfchar + self.htchar * indent)

    def format_list(self, value, indent):
        items = [
            self.lfchar + self.htchar * (indent + 1) + (self.types[type(item) if type(item) in self.types else object])(self, item, indent + 1)
            for item in value
        ]
        return '[%s]' % (','.join(items) + self.lfchar + self.htchar * indent)

    def format_tuple(self, value, indent):
        items = [
            self.lfchar + self.htchar * (indent + 1) + (self.types[type(item) if type(item) in self.types else object])(self, item, indent + 1)
            for item in value
        ]
        return '(%s)' % (','.join(items) + self.lfchar + self.htchar * indent)

To initialize it :

pretty = Formatter()

It can support the addition of formatters for defined types, you simply need to make a function for that like this one and bind it to the type you want with set_formater :

from collections import OrderedDict

def format_ordereddict(self, value, indent):
    items = [
        self.lfchar + self.htchar * (indent + 1) +
        "(" + repr(key) + ', ' + (self.types[
            type(value[key]) if type(value[key]) in self.types else object
        ])(self, value[key], indent + 1) + ")"
        for key in value
    ]
    return 'OrderedDict([%s])' % (','.join(items) +
           self.lfchar + self.htchar * indent)
pretty.set_formater(OrderedDict, format_ordereddict)

For historical reasons, I keep the previous pretty printer which was a function instead of a class, but they both can be used the same way, the class version simply permit much more :

def pretty(value, htchar='\t', lfchar='\n', indent=0):
    nlch = lfchar + htchar * (indent + 1)
    if type(value) is dict:
        items = [
            nlch + repr(key) + ': ' + pretty(value[key], htchar, lfchar, indent + 1)
            for key in value
        ]
        return '{%s}' % (','.join(items) + lfchar + htchar * indent)
    elif type(value) is list:
        items = [
            nlch + pretty(item, htchar, lfchar, indent + 1)
            for item in value
        ]
        return '[%s]' % (','.join(items) + lfchar + htchar * indent)
    elif type(value) is tuple:
        items = [
            nlch + pretty(item, htchar, lfchar, indent + 1)
            for item in value
        ]
        return '(%s)' % (','.join(items) + lfchar + htchar * indent)
    else:
        return repr(value)

To use it :

>>> a = {'list':['a','b',1,2],'dict':{'a':1,2:'b'},'tuple':('a','b',1,2),'function':pretty,'unicode':u'\xa7',("tuple","key"):"valid"}
>>> a
{'function': <function pretty at 0x7fdf555809b0>, 'tuple': ('a', 'b', 1, 2), 'list': ['a', 'b', 1, 2], 'dict': {'a': 1, 2: 'b'}, 'unicode': u'\xa7', ('tuple', 'key'): 'valid'}
>>> print(pretty(a))
{
    'function': <function pretty at 0x7fdf555809b0>,
    'tuple': (
        'a',
        'b',
        1,
        2
    ),
    'list': [
        'a',
        'b',
        1,
        2
    ],
    'dict': {
        'a': 1,
        2: 'b'
    },
    'unicode': u'\xa7',
    ('tuple', 'key'): 'valid'
}

Compared to other versions :

  • This solution looks directly for object type, so you can pretty print almost everything, not only list or dict.
  • Doesn’t have any dependancy.
  • Everything is put inside a string, so you can do whatever you want with it.
  • The class and the function has been tested and works with Python 2.7 and 3.4.
  • You can have all type of objects inside, this is their representations and not theirs contents that being put in the result (so string have quotes, Unicode string are fully represented …).
  • With the class version, you can add formatting for every object type you want or change them for already defined ones.
  • key can be of any valid type.
  • Indent and Newline character can be changed for everything we’d like.
  • Dict, List and Tuples are pretty printed.

回答 4

通过这种方式,您可以以漂亮的方式打印它,例如您的词典名称是yasin

import json

print (json.dumps(yasin, indent=2))

By this way you can print it in pretty way for example your dictionary name is yasin

import json

print (json.dumps(yasin, indent=2))

回答 5

另一种选择yapf

from pprint import pformat
from yapf.yapflib.yapf_api import FormatCode

dict_example = {'1': '1', '2': '2', '3': [1, 2, 3, 4, 5], '4': {'1': '1', '2': '2', '3': [1, 2, 3, 4, 5]}}
dict_string = pformat(dict_example)
formatted_code, _ = FormatCode(dict_string)

print(formatted_code)

输出:

{
    '1': '1',
    '2': '2',
    '3': [1, 2, 3, 4, 5],
    '4': {
        '1': '1',
        '2': '2',
        '3': [1, 2, 3, 4, 5]
    }
}

Another option with yapf:

from pprint import pformat
from yapf.yapflib.yapf_api import FormatCode

dict_example = {'1': '1', '2': '2', '3': [1, 2, 3, 4, 5], '4': {'1': '1', '2': '2', '3': [1, 2, 3, 4, 5]}}
dict_string = pformat(dict_example)
formatted_code, _ = FormatCode(dict_string)

print(formatted_code)

Output:

{
    '1': '1',
    '2': '2',
    '3': [1, 2, 3, 4, 5],
    '4': {
        '1': '1',
        '2': '2',
        '3': [1, 2, 3, 4, 5]
    }
}

回答 6

正如其他人所发表的,您可以使用递归/ dfs打印嵌套的字典数据,如果它是字典,则可以递归调用;否则打印数据。

def print_json(data):
    if type(data) == dict:
            for k, v in data.items():
                    print k
                    print_json(v)
    else:
            print data

As others have posted, you can use recursion/dfs to print the nested dictionary data and call recursively if it is a dictionary; otherwise print the data.

def print_json(data):
    if type(data) == dict:
            for k, v in data.items():
                    print k
                    print_json(v)
    else:
            print data

回答 7

pout可以漂亮地打印出您扔给它的任何东西,例如(data从另一个答案中借用):

data = {'a':2, 'b':{'x':3, 'y':{'t1': 4, 't2':5}}}
pout.vs(data)

将导致输出打印到屏幕上,例如:

{
    'a': 2,
    'b':
    {
        'y':
        {
            't2': 5,
            't1': 4
        },
        'x': 3
    }
}

或者,您可以返回对象的格式化字符串输出:

v = pout.s(data)

它的主要用例是用于调试,因此它不会阻塞对象实例或任何事物,它可以处理unicode输出,可以在python 2.7和3中工作。

披露:我是pout的作者和维护者。

pout can pretty print anything you throw at it, for example (borrowing data from another answer):

data = {'a':2, 'b':{'x':3, 'y':{'t1': 4, 't2':5}}}
pout.vs(data)

would result in output printed to the screen like:

{
    'a': 2,
    'b':
    {
        'y':
        {
            't2': 5,
            't1': 4
        },
        'x': 3
    }
}

or you can return the formatted string output of your object:

v = pout.s(data)

Its primary use case is for debugging so it doesn’t choke on object instances or anything and it handles unicode output as you would expect, works in python 2.7 and 3.

disclosure: I’m the author and maintainer of pout.


回答 8

最有效的方法之一就是使用已经构建的pprint模块。

定义打印深度所需的参数如您所料 depth

import pprint
pp = pprint.PrettyPrinter(depth=4)
pp.pprint(mydict)

而已 !

One of the most pythonic ways for that is to use the already build pprint module.

The argument that you need for define the print depth is as you may expect depth

import pprint
pp = pprint.PrettyPrinter(depth=4)
pp.pprint(mydict)

That’s it !


回答 9

我听了sth的回答,并对其做了一些修改,以适应嵌套字典和列表的需求:

def pretty(d, indent=0):
    if isinstance(d, dict):
        for key, value in d.iteritems():
            print '\t' * indent + str(key)
            if isinstance(value, dict) or isinstance(value, list):
                pretty(value, indent+1)
            else:
                print '\t' * (indent+1) + str(value)
    elif isinstance(d, list):
        for item in d:
            if isinstance(item, dict) or isinstance(item, list):
                pretty(item, indent+1)
            else:
                print '\t' * (indent+1) + str(item)
    else:
        pass

然后给我的输出像:

>>> 
xs:schema
    @xmlns:xs
        http://www.w3.org/2001/XMLSchema
    xs:redefine
        @schemaLocation
            base.xsd
        xs:complexType
            @name
                Extension
            xs:complexContent
                xs:restriction
                    @base
                        Extension
                    xs:sequence
                        xs:element
                            @name
                                Policy
                            @minOccurs
                                1
                            xs:complexType
                                xs:sequence
                                    xs:element
                                            ...

I took sth’s answer and modified it slightly to fit my needs of a nested dictionaries and lists:

def pretty(d, indent=0):
    if isinstance(d, dict):
        for key, value in d.iteritems():
            print '\t' * indent + str(key)
            if isinstance(value, dict) or isinstance(value, list):
                pretty(value, indent+1)
            else:
                print '\t' * (indent+1) + str(value)
    elif isinstance(d, list):
        for item in d:
            if isinstance(item, dict) or isinstance(item, list):
                pretty(item, indent+1)
            else:
                print '\t' * (indent+1) + str(item)
    else:
        pass

Which then gives me output like:

>>> 
xs:schema
    @xmlns:xs
        http://www.w3.org/2001/XMLSchema
    xs:redefine
        @schemaLocation
            base.xsd
        xs:complexType
            @name
                Extension
            xs:complexContent
                xs:restriction
                    @base
                        Extension
                    xs:sequence
                        xs:element
                            @name
                                Policy
                            @minOccurs
                                1
                            xs:complexType
                                xs:sequence
                                    xs:element
                                            ...

回答 10

……我下沉那很漂亮;)

def pretty(d, indent=0):
    for key, value in d.iteritems():
        if isinstance(value, dict):
            print '\t' * indent + (("%30s: {\n") % str(key).upper())
            pretty(value, indent+1)
            print '\t' * indent + ' ' * 32 + ('} # end of %s #\n' % str(key).upper())
        elif isinstance(value, list):
            for val in value:
                print '\t' * indent + (("%30s: [\n") % str(key).upper())
                pretty(val, indent+1)
                print '\t' * indent + ' ' * 32 + ('] # end of %s #\n' % str(key).upper())
        else:
            print '\t' * indent + (("%30s: %s") % (str(key).upper(),str(value)))

Sth, i sink that’s pretty ;)

def pretty(d, indent=0):
    for key, value in d.iteritems():
        if isinstance(value, dict):
            print '\t' * indent + (("%30s: {\n") % str(key).upper())
            pretty(value, indent+1)
            print '\t' * indent + ' ' * 32 + ('} # end of %s #\n' % str(key).upper())
        elif isinstance(value, list):
            for val in value:
                print '\t' * indent + (("%30s: [\n") % str(key).upper())
                pretty(val, indent+1)
                print '\t' * indent + ' ' * 32 + ('] # end of %s #\n' % str(key).upper())
        else:
            print '\t' * indent + (("%30s: %s") % (str(key).upper(),str(value)))

回答 11

This class prints out a complex nested dictionary with sub dictionaries and sub lists.  
##
## Recursive class to parse and print complex nested dictionary
##

class NestedDictionary(object):
    def __init__(self,value):
        self.value=value

    def print(self,depth):
        spacer="--------------------"
        if type(self.value)==type(dict()):
            for kk, vv in self.value.items():
                if (type(vv)==type(dict())):
                    print(spacer[:depth],kk)
                    vvv=(NestedDictionary(vv))
                    depth=depth+3
                    vvv.print(depth)
                    depth=depth-3
                else:
                    if (type(vv)==type(list())):
                        for i in vv:
                            vvv=(NestedDictionary(i))
                            depth=depth+3
                            vvv.print(depth)
                            depth=depth-3
                    else:
                        print(spacer[:depth],kk,vv) 

##
## Instatiate and execute - this prints complex nested dictionaries
## with sub dictionaries and sub lists
## 'something' is a complex nested dictionary

MyNest=NestedDictionary(weather_com_result)
MyNest.print(0)
This class prints out a complex nested dictionary with sub dictionaries and sub lists.  
##
## Recursive class to parse and print complex nested dictionary
##

class NestedDictionary(object):
    def __init__(self,value):
        self.value=value

    def print(self,depth):
        spacer="--------------------"
        if type(self.value)==type(dict()):
            for kk, vv in self.value.items():
                if (type(vv)==type(dict())):
                    print(spacer[:depth],kk)
                    vvv=(NestedDictionary(vv))
                    depth=depth+3
                    vvv.print(depth)
                    depth=depth-3
                else:
                    if (type(vv)==type(list())):
                        for i in vv:
                            vvv=(NestedDictionary(i))
                            depth=depth+3
                            vvv.print(depth)
                            depth=depth-3
                    else:
                        print(spacer[:depth],kk,vv) 

##
## Instatiate and execute - this prints complex nested dictionaries
## with sub dictionaries and sub lists
## 'something' is a complex nested dictionary

MyNest=NestedDictionary(weather_com_result)
MyNest.print(0)

回答 12

我写了这个简单的代码来打印Python中json对象的一般结构。

def getstructure(data, tab = 0):
    if type(data) is dict:
        print ' '*tab + '{' 
        for key in data:
            print ' '*tab + '  ' + key + ':'
            getstructure(data[key], tab+4)
        print ' '*tab + '}'         
    elif type(data) is list and len(data) > 0:
        print ' '*tab + '['
        getstructure(data[0], tab+4)
        print ' '*tab + '  ...'
        print ' '*tab + ']'

以下数据的结果

a = {'list':['a','b',1,2],'dict':{'a':1,2:'b'},'tuple':('a','b',1,2),'function':'p','unicode':u'\xa7',("tuple","key"):"valid"}
getstructure(a)

非常紧凑,看起来像这样:

{
  function:
  tuple:
  list:
    [
      ...
    ]
  dict:
    {
      a:
      2:
    }
  unicode:
  ('tuple', 'key'):
}

I wrote this simple code to print the general structure of a json object in Python.

def getstructure(data, tab = 0):
    if type(data) is dict:
        print ' '*tab + '{' 
        for key in data:
            print ' '*tab + '  ' + key + ':'
            getstructure(data[key], tab+4)
        print ' '*tab + '}'         
    elif type(data) is list and len(data) > 0:
        print ' '*tab + '['
        getstructure(data[0], tab+4)
        print ' '*tab + '  ...'
        print ' '*tab + ']'

the result for the following data

a = {'list':['a','b',1,2],'dict':{'a':1,2:'b'},'tuple':('a','b',1,2),'function':'p','unicode':u'\xa7',("tuple","key"):"valid"}
getstructure(a)

is very compact and looks like this:

{
  function:
  tuple:
  list:
    [
      ...
    ]
  dict:
    {
      a:
      2:
    }
  unicode:
  ('tuple', 'key'):
}

回答 13

我本人是一位相对较新的python新手,但过去两周来我一直在使用嵌套字典,而这正是我想出的。

您应该尝试使用堆栈。使根字典中的键成为列表的列表:

stack = [ root.keys() ]     # Result: [ [root keys] ]

从最后到第一个相反的顺序,查找字典中的每个键,以查看其值是否也是字典。如果不是,请打印密钥,然后将其删除。但是,如果键的值字典,请打印键,然后将该值的键附加到堆栈的末尾,并以相同的方式开始处理该列表,对每个新的键列表进行递归重复。

如果每个列表中第二个键的值是一个字典,则在几轮之后您将得到类似以下的内容:

[['key 1','key 2'],['key 2.1','key 2.2'],['key 2.2.1','key 2.2.2'],[`etc.`]]

这种方法的好处是缩进量只是\t堆栈长度的倍数:

indent = "\t" * len(stack)

缺点是,为了检查每个键,您需要将其散列到相关的子词典,尽管这可以通过列表理解和简单的for循环轻松地进行处理:

path = [li[-1] for li in stack]
# The last key of every list of keys in the stack

sub = root
for p in path:
    sub = sub[p]


if type(sub) == dict:
    stack.append(sub.keys()) # And so on

请注意,这种方法将需要您清理尾随的空列表,删除任何列表中的最后一个键,然后再删除一个空列表(当然这可能会创建另一个空列表,依此类推)。

还有其他方法可以实现此方法,但希望它为您提供了如何执行此操作的基本思路。

编辑:如果您不想遍历所有内容,该pprint模块将以一种不错的格式打印嵌套字典。

I’m a relative python newbie myself but I’ve been working with nested dictionaries for the past couple weeks and this is what I had came up with.

You should try using a stack. Make the keys from the root dictionary into a list of a list:

stack = [ root.keys() ]     # Result: [ [root keys] ]

Going in reverse order from last to first, lookup each key in the dictionary to see if its value is (also) a dictionary. If not, print the key then delete it. However if the value for the key is a dictionary, print the key then append the keys for that value to the end of the stack, and start processing that list in the same way, repeating recursively for each new list of keys.

If the value for the second key in each list were a dictionary you would have something like this after several rounds:

[['key 1','key 2'],['key 2.1','key 2.2'],['key 2.2.1','key 2.2.2'],[`etc.`]]

The upside to this approach is that the indent is just \t times the length of the stack:

indent = "\t" * len(stack)

The downside is that in order to check each key you need to hash through to the relevant sub-dictionary, though this can be handled easily with a list comprehension and a simple for loop:

path = [li[-1] for li in stack]
# The last key of every list of keys in the stack

sub = root
for p in path:
    sub = sub[p]


if type(sub) == dict:
    stack.append(sub.keys()) # And so on

Be aware that this approach will require you to cleanup trailing empty lists, and to delete the last key in any list followed by an empty list (which of course may create another empty list, and so on).

There are other ways to implement this approach but hopefully this gives you a basic idea of how to do it.

EDIT: If you don’t want to go through all that, the pprint module prints nested dictionaries in a nice format.


回答 14

这是我根据某人的评论编写的函数。它的工作原理与带缩进的json.dumps相同,但是我使用的是制表符而不是缩进的空间。在Python 3.2+中,您可以直接将缩进指定为’\ t’,但在2.7中则不能。

def pretty_dict(d):
    def pretty(d, indent):
        for i, (key, value) in enumerate(d.iteritems()):
            if isinstance(value, dict):
                print '{0}"{1}": {{'.format( '\t' * indent, str(key))
                pretty(value, indent+1)
                if i == len(d)-1:
                    print '{0}}}'.format( '\t' * indent)
                else:
                    print '{0}}},'.format( '\t' * indent)
            else:
                if i == len(d)-1:
                    print '{0}"{1}": "{2}"'.format( '\t' * indent, str(key), value)
                else:
                    print '{0}"{1}": "{2}",'.format( '\t' * indent, str(key), value)
    print '{'
    pretty(d,indent=1)
    print '}'

例如:

>>> dict_var = {'a':2, 'b':{'x':3, 'y':{'t1': 4, 't2':5}}}
>>> pretty_dict(dict_var)
{
    "a": "2",
    "b": {
        "y": {
            "t2": "5",
            "t1": "4"
        },
        "x": "3"
    }
}

Here’s a function I wrote based on what sth’s comment. It’s works the same as json.dumps with indent, but I’m using tabs instead of space for indents. In Python 3.2+ you can specify indent to be a ‘\t’ directly, but not in 2.7.

def pretty_dict(d):
    def pretty(d, indent):
        for i, (key, value) in enumerate(d.iteritems()):
            if isinstance(value, dict):
                print '{0}"{1}": {{'.format( '\t' * indent, str(key))
                pretty(value, indent+1)
                if i == len(d)-1:
                    print '{0}}}'.format( '\t' * indent)
                else:
                    print '{0}}},'.format( '\t' * indent)
            else:
                if i == len(d)-1:
                    print '{0}"{1}": "{2}"'.format( '\t' * indent, str(key), value)
                else:
                    print '{0}"{1}": "{2}",'.format( '\t' * indent, str(key), value)
    print '{'
    pretty(d,indent=1)
    print '}'

Ex:

>>> dict_var = {'a':2, 'b':{'x':3, 'y':{'t1': 4, 't2':5}}}
>>> pretty_dict(dict_var)
{
    "a": "2",
    "b": {
        "y": {
            "t2": "5",
            "t1": "4"
        },
        "x": "3"
    }
}

回答 15

这将打印任何形式的嵌套字典,同时跟踪整个“父”字典。

dicList = list()

def prettierPrint(dic, dicList):
count = 0
for key, value in dic.iteritems():
    count+=1
    if str(value) == 'OrderedDict()':
        value = None
    if not isinstance(value, dict):
        print str(key) + ": " + str(value)
        print str(key) + ' was found in the following path:',
        print dicList
        print '\n'
    elif isinstance(value, dict):
        dicList.append(key)
        prettierPrint(value, dicList)
    if dicList:
         if count == len(dic):
             dicList.pop()
             count = 0

prettierPrint(dicExample, dicList)

这是根据不同格式进行打印的良好起点,例如OP中指定的格式。您真正需要做的就是围绕“ 打印”块进行操作。请注意,它会查看该值是否为“ OrderedDict()”。根据您是否使用的是Container数据类型Collections中的某些内容,应进行此类故障保护,以使elif块由于其名称而不会将其视为其他字典。截至目前,示例字典

example_dict = {'key1': 'value1',
            'key2': 'value2',
            'key3': {'key3a': 'value3a'},
            'key4': {'key4a': {'key4aa': 'value4aa',
                               'key4ab': 'value4ab',
                               'key4ac': 'value4ac'},
                     'key4b': 'value4b'}

将打印

key3a: value3a
key3a was found in the following path: ['key3']

key2: value2
key2 was found in the following path: []

key1: value1
key1 was found in the following path: []

key4ab: value4ab
key4ab was found in the following path: ['key4', 'key4a']

key4ac: value4ac
key4ac was found in the following path: ['key4', 'key4a']

key4aa: value4aa
key4aa was found in the following path: ['key4', 'key4a']

key4b: value4b
key4b was found in the following path: ['key4']

〜更改代码以适合问题的格式〜

lastDict = list()
dicList = list()
def prettierPrint(dic, dicList):
    global lastDict
    count = 0
    for key, value in dic.iteritems():
        count+=1
        if str(value) == 'OrderedDict()':
            value = None
        if not isinstance(value, dict):
            if lastDict == dicList:
                sameParents = True
            else:
                sameParents = False

            if dicList and sameParents is not True:
                spacing = ' ' * len(str(dicList))
                print dicList
                print spacing,
                print str(value)

            if dicList and sameParents is True:
                print spacing,
                print str(value)
            lastDict = list(dicList)

        elif isinstance(value, dict):
            dicList.append(key)
            prettierPrint(value, dicList)

        if dicList:
             if count == len(dic):
                 dicList.pop()
                 count = 0

使用相同的示例代码,它将打印以下内容:

['key3']
         value3a
['key4', 'key4a']
                  value4ab
                  value4ac
                  value4aa
['key4']
         value4b

这不正是什么要求在OP。不同之处在于,仍然打印出parent ^ n,而不是不打印并替换为空白。要获得OP的格式,您需要执行以下操作:将dicListlastDict进行迭代比较。您可以通过一个新的字典和dicList的内容复制到它做到这一点,检查,如果在复制的字典是一样的在lastDict,和-如果它是-书写空白到使用字符串倍增器功能定位。

Here’s something that will print any sort of nested dictionary, while keeping track of the “parent” dictionaries along the way.

dicList = list()

def prettierPrint(dic, dicList):
count = 0
for key, value in dic.iteritems():
    count+=1
    if str(value) == 'OrderedDict()':
        value = None
    if not isinstance(value, dict):
        print str(key) + ": " + str(value)
        print str(key) + ' was found in the following path:',
        print dicList
        print '\n'
    elif isinstance(value, dict):
        dicList.append(key)
        prettierPrint(value, dicList)
    if dicList:
         if count == len(dic):
             dicList.pop()
             count = 0

prettierPrint(dicExample, dicList)

This is a good starting point for printing according to different formats, like the one specified in OP. All you really need to do is operations around the Print blocks. Note that it looks to see if the value is ‘OrderedDict()’. Depending on whether you’re using something from Container datatypes Collections, you should make these sort of fail-safes so the elif block doesn’t see it as an additional dictionary due to its name. As of now, an example dictionary like

example_dict = {'key1': 'value1',
            'key2': 'value2',
            'key3': {'key3a': 'value3a'},
            'key4': {'key4a': {'key4aa': 'value4aa',
                               'key4ab': 'value4ab',
                               'key4ac': 'value4ac'},
                     'key4b': 'value4b'}

will print

key3a: value3a
key3a was found in the following path: ['key3']

key2: value2
key2 was found in the following path: []

key1: value1
key1 was found in the following path: []

key4ab: value4ab
key4ab was found in the following path: ['key4', 'key4a']

key4ac: value4ac
key4ac was found in the following path: ['key4', 'key4a']

key4aa: value4aa
key4aa was found in the following path: ['key4', 'key4a']

key4b: value4b
key4b was found in the following path: ['key4']

~altering code to fit the question’s format~

lastDict = list()
dicList = list()
def prettierPrint(dic, dicList):
    global lastDict
    count = 0
    for key, value in dic.iteritems():
        count+=1
        if str(value) == 'OrderedDict()':
            value = None
        if not isinstance(value, dict):
            if lastDict == dicList:
                sameParents = True
            else:
                sameParents = False

            if dicList and sameParents is not True:
                spacing = ' ' * len(str(dicList))
                print dicList
                print spacing,
                print str(value)

            if dicList and sameParents is True:
                print spacing,
                print str(value)
            lastDict = list(dicList)

        elif isinstance(value, dict):
            dicList.append(key)
            prettierPrint(value, dicList)

        if dicList:
             if count == len(dic):
                 dicList.pop()
                 count = 0

Using the same example code, it will print the following:

['key3']
         value3a
['key4', 'key4a']
                  value4ab
                  value4ac
                  value4aa
['key4']
         value4b

This isn’t exactly what is requested in OP. The difference is that a parent^n is still printed, instead of being absent and replaced with white-space. To get to OP’s format, you’ll need to do something like the following: iteratively compare dicList with the lastDict. You can do this by making a new dictionary and copying dicList’s content to it, checking if i in the copied dictionary is the same as i in lastDict, and — if it is — writing whitespace to that i position using the string multiplier function.


回答 16

从此链接

def prnDict(aDict, br='\n', html=0,
            keyAlign='l',   sortKey=0,
            keyPrefix='',   keySuffix='',
            valuePrefix='', valueSuffix='',
            leftMargin=0,   indent=1 ):
    '''
return a string representive of aDict in the following format:
    {
     key1: value1,
     key2: value2,
     ...
     }

Spaces will be added to the keys to make them have same width.

sortKey: set to 1 if want keys sorted;
keyAlign: either 'l' or 'r', for left, right align, respectively.
keyPrefix, keySuffix, valuePrefix, valueSuffix: The prefix and
   suffix to wrap the keys or values. Good for formatting them
   for html document(for example, keyPrefix='<b>', keySuffix='</b>'). 
   Note: The keys will be padded with spaces to have them
         equally-wide. The pre- and suffix will be added OUTSIDE
         the entire width.
html: if set to 1, all spaces will be replaced with '&nbsp;', and
      the entire output will be wrapped with '<code>' and '</code>'.
br: determine the carriage return. If html, it is suggested to set
    br to '<br>'. If you want the html source code eazy to read,
    set br to '<br>\n'

version: 04b52
author : Runsun Pan
require: odict() # an ordered dict, if you want the keys sorted.
         Dave Benjamin 
         http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/161403
    '''

    if aDict:

        #------------------------------ sort key
        if sortKey:
            dic = aDict.copy()
            keys = dic.keys()
            keys.sort()
            aDict = odict()
            for k in keys:
                aDict[k] = dic[k]

        #------------------- wrap keys with ' ' (quotes) if str
        tmp = ['{']
        ks = [type(x)==str and "'%s'"%x or x for x in aDict.keys()]

        #------------------- wrap values with ' ' (quotes) if str
        vs = [type(x)==str and "'%s'"%x or x for x in aDict.values()] 

        maxKeyLen = max([len(str(x)) for x in ks])

        for i in range(len(ks)):

            #-------------------------- Adjust key width
            k = {1            : str(ks[i]).ljust(maxKeyLen),
                 keyAlign=='r': str(ks[i]).rjust(maxKeyLen) }[1]

            v = vs[i]        
            tmp.append(' '* indent+ '%s%s%s:%s%s%s,' %(
                        keyPrefix, k, keySuffix,
                        valuePrefix,v,valueSuffix))

        tmp[-1] = tmp[-1][:-1] # remove the ',' in the last item
        tmp.append('}')

        if leftMargin:
          tmp = [ ' '*leftMargin + x for x in tmp ]

        if html:
            return '<code>%s</code>' %br.join(tmp).replace(' ','&nbsp;')
        else:
            return br.join(tmp)     
    else:
        return '{}'

'''
Example:

>>> a={'C': 2, 'B': 1, 'E': 4, (3, 5): 0}

>>> print prnDict(a)
{
 'C'   :2,
 'B'   :1,
 'E'   :4,
 (3, 5):0
}

>>> print prnDict(a, sortKey=1)
{
 'B'   :1,
 'C'   :2,
 'E'   :4,
 (3, 5):0
}

>>> print prnDict(a, keyPrefix="<b>", keySuffix="</b>")
{
 <b>'C'   </b>:2,
 <b>'B'   </b>:1,
 <b>'E'   </b>:4,
 <b>(3, 5)</b>:0
}

>>> print prnDict(a, html=1)
<code>{
&nbsp;'C'&nbsp;&nbsp;&nbsp;:2,
&nbsp;'B'&nbsp;&nbsp;&nbsp;:1,
&nbsp;'E'&nbsp;&nbsp;&nbsp;:4,
&nbsp;(3,&nbsp;5):0
}</code>

>>> b={'car': [6, 6, 12], 'about': [15, 9, 6], 'bookKeeper': [9, 9, 15]}

>>> print prnDict(b, sortKey=1)
{
 'about'     :[15, 9, 6],
 'bookKeeper':[9, 9, 15],
 'car'       :[6, 6, 12]
}

>>> print prnDict(b, keyAlign="r")
{
        'car':[6, 6, 12],
      'about':[15, 9, 6],
 'bookKeeper':[9, 9, 15]
}
'''

From this link:

def prnDict(aDict, br='\n', html=0,
            keyAlign='l',   sortKey=0,
            keyPrefix='',   keySuffix='',
            valuePrefix='', valueSuffix='',
            leftMargin=0,   indent=1 ):
    '''
return a string representive of aDict in the following format:
    {
     key1: value1,
     key2: value2,
     ...
     }

Spaces will be added to the keys to make them have same width.

sortKey: set to 1 if want keys sorted;
keyAlign: either 'l' or 'r', for left, right align, respectively.
keyPrefix, keySuffix, valuePrefix, valueSuffix: The prefix and
   suffix to wrap the keys or values. Good for formatting them
   for html document(for example, keyPrefix='<b>', keySuffix='</b>'). 
   Note: The keys will be padded with spaces to have them
         equally-wide. The pre- and suffix will be added OUTSIDE
         the entire width.
html: if set to 1, all spaces will be replaced with '&nbsp;', and
      the entire output will be wrapped with '<code>' and '</code>'.
br: determine the carriage return. If html, it is suggested to set
    br to '<br>'. If you want the html source code eazy to read,
    set br to '<br>\n'

version: 04b52
author : Runsun Pan
require: odict() # an ordered dict, if you want the keys sorted.
         Dave Benjamin 
         http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/161403
    '''

    if aDict:

        #------------------------------ sort key
        if sortKey:
            dic = aDict.copy()
            keys = dic.keys()
            keys.sort()
            aDict = odict()
            for k in keys:
                aDict[k] = dic[k]

        #------------------- wrap keys with ' ' (quotes) if str
        tmp = ['{']
        ks = [type(x)==str and "'%s'"%x or x for x in aDict.keys()]

        #------------------- wrap values with ' ' (quotes) if str
        vs = [type(x)==str and "'%s'"%x or x for x in aDict.values()] 

        maxKeyLen = max([len(str(x)) for x in ks])

        for i in range(len(ks)):

            #-------------------------- Adjust key width
            k = {1            : str(ks[i]).ljust(maxKeyLen),
                 keyAlign=='r': str(ks[i]).rjust(maxKeyLen) }[1]

            v = vs[i]        
            tmp.append(' '* indent+ '%s%s%s:%s%s%s,' %(
                        keyPrefix, k, keySuffix,
                        valuePrefix,v,valueSuffix))

        tmp[-1] = tmp[-1][:-1] # remove the ',' in the last item
        tmp.append('}')

        if leftMargin:
          tmp = [ ' '*leftMargin + x for x in tmp ]

        if html:
            return '<code>%s</code>' %br.join(tmp).replace(' ','&nbsp;')
        else:
            return br.join(tmp)     
    else:
        return '{}'

'''
Example:

>>> a={'C': 2, 'B': 1, 'E': 4, (3, 5): 0}

>>> print prnDict(a)
{
 'C'   :2,
 'B'   :1,
 'E'   :4,
 (3, 5):0
}

>>> print prnDict(a, sortKey=1)
{
 'B'   :1,
 'C'   :2,
 'E'   :4,
 (3, 5):0
}

>>> print prnDict(a, keyPrefix="<b>", keySuffix="</b>")
{
 <b>'C'   </b>:2,
 <b>'B'   </b>:1,
 <b>'E'   </b>:4,
 <b>(3, 5)</b>:0
}

>>> print prnDict(a, html=1)
<code>{
&nbsp;'C'&nbsp;&nbsp;&nbsp;:2,
&nbsp;'B'&nbsp;&nbsp;&nbsp;:1,
&nbsp;'E'&nbsp;&nbsp;&nbsp;:4,
&nbsp;(3,&nbsp;5):0
}</code>

>>> b={'car': [6, 6, 12], 'about': [15, 9, 6], 'bookKeeper': [9, 9, 15]}

>>> print prnDict(b, sortKey=1)
{
 'about'     :[15, 9, 6],
 'bookKeeper':[9, 9, 15],
 'car'       :[6, 6, 12]
}

>>> print prnDict(b, keyAlign="r")
{
        'car':[6, 6, 12],
      'about':[15, 9, 6],
 'bookKeeper':[9, 9, 15]
}
'''

回答 17

我只是在回答了sth的答案并做了一个很小但非常有用的修改后才回到这个问题。此函数将打印JSON树中的所有以及该树中叶节点大小

def print_JSON_tree(d, indent=0):
    for key, value in d.iteritems():
        print '    ' * indent + unicode(key),
        if isinstance(value, dict):
            print; print_JSON_tree(value, indent+1)
        else:
            print ":", str(type(d[key])).split("'")[1], "-", str(len(unicode(d[key])))

当您有大型JSON对象并想弄清楚肉在哪里时,这非常好。范例

>>> print_JSON_tree(JSON_object)
key1
    value1 : int - 5
    value2 : str - 16
    key2
       value1 : str - 34
       value2 : list - 5623456

这将告诉您,您关心的大多数数据可能都在内部,JSON_object['key1']['key2']['value2']因为格式化为字符串的值的长度非常大。

I’m just returning to this question after taking sth‘s answer and making a small but very useful modification. This function prints all keys in the JSON tree as well as the size of leaf nodes in that tree.

def print_JSON_tree(d, indent=0):
    for key, value in d.iteritems():
        print '    ' * indent + unicode(key),
        if isinstance(value, dict):
            print; print_JSON_tree(value, indent+1)
        else:
            print ":", str(type(d[key])).split("'")[1], "-", str(len(unicode(d[key])))

It’s really nice when you have large JSON objects and want to figure out where the meat is. Example:

>>> print_JSON_tree(JSON_object)
key1
    value1 : int - 5
    value2 : str - 16
    key2
       value1 : str - 34
       value2 : list - 5623456

This would tell you that most of the data you care about is probably inside JSON_object['key1']['key2']['value2'] because the length of that value formatted as a string is very large.


回答 18

使用此功能:

def pretty_dict(d, n=1):
    for k in d:
        print(" "*n + k)
        try:
            pretty_dict(d[k], n=n+4)
        except TypeError:
            continue

这样称呼它:

pretty_dict(mydict)

Use this function:

def pretty_dict(d, n=1):
    for k in d:
        print(" "*n + k)
        try:
            pretty_dict(d[k], n=n+4)
        except TypeError:
            continue

Call it like this:

pretty_dict(mydict)

回答 19

这是我在处理需要在.txt文件中编写字典的类时想到的:

@staticmethod
def _pretty_write_dict(dictionary):

    def _nested(obj, level=1):
        indentation_values = "\t" * level
        indentation_braces = "\t" * (level - 1)
        if isinstance(obj, dict):
            return "{\n%(body)s%(indent_braces)s}" % {
                "body": "".join("%(indent_values)s\'%(key)s\': %(value)s,\n" % {
                    "key": str(key),
                    "value": _nested(value, level + 1),
                    "indent_values": indentation_values
                } for key, value in obj.items()),
                "indent_braces": indentation_braces
            }
        if isinstance(obj, list):
            return "[\n%(body)s\n%(indent_braces)s]" % {
                "body": "".join("%(indent_values)s%(value)s,\n" % {
                    "value": _nested(value, level + 1),
                    "indent_values": indentation_values
                } for value in obj),
                "indent_braces": indentation_braces
            }
        else:
            return "\'%(value)s\'" % {"value": str(obj)}

    dict_text = _nested(dictionary)
    return dict_text

现在,如果我们有这样的字典:

some_dict = {'default': {'ENGINE': [1, 2, 3, {'some_key': {'some_other_key': 'some_value'}}], 'NAME': 'some_db_name', 'PORT': '', 'HOST': 'localhost', 'USER': 'some_user_name', 'PASSWORD': 'some_password', 'OPTIONS': {'init_command': 'SET foreign_key_checks = 0;'}}}

我们做:

print(_pretty_write_dict(some_dict))

我们得到:

{
    'default': {
        'ENGINE': [
            '1',
            '2',
            '3',
            {
                'some_key': {
                    'some_other_key': 'some_value',
                },
            },
        ],
        'NAME': 'some_db_name',
        'OPTIONS': {
            'init_command': 'SET foreign_key_checks = 0;',
        },
        'HOST': 'localhost',
        'USER': 'some_user_name',
        'PASSWORD': 'some_password',
        'PORT': '',
    },
}

This is what I came up with while working on a class that needed to write a dictionary in a .txt file:

@staticmethod
def _pretty_write_dict(dictionary):

    def _nested(obj, level=1):
        indentation_values = "\t" * level
        indentation_braces = "\t" * (level - 1)
        if isinstance(obj, dict):
            return "{\n%(body)s%(indent_braces)s}" % {
                "body": "".join("%(indent_values)s\'%(key)s\': %(value)s,\n" % {
                    "key": str(key),
                    "value": _nested(value, level + 1),
                    "indent_values": indentation_values
                } for key, value in obj.items()),
                "indent_braces": indentation_braces
            }
        if isinstance(obj, list):
            return "[\n%(body)s\n%(indent_braces)s]" % {
                "body": "".join("%(indent_values)s%(value)s,\n" % {
                    "value": _nested(value, level + 1),
                    "indent_values": indentation_values
                } for value in obj),
                "indent_braces": indentation_braces
            }
        else:
            return "\'%(value)s\'" % {"value": str(obj)}

    dict_text = _nested(dictionary)
    return dict_text

Now, if we have a dictionary like this:

some_dict = {'default': {'ENGINE': [1, 2, 3, {'some_key': {'some_other_key': 'some_value'}}], 'NAME': 'some_db_name', 'PORT': '', 'HOST': 'localhost', 'USER': 'some_user_name', 'PASSWORD': 'some_password', 'OPTIONS': {'init_command': 'SET foreign_key_checks = 0;'}}}

And we do:

print(_pretty_write_dict(some_dict))

We get:

{
    'default': {
        'ENGINE': [
            '1',
            '2',
            '3',
            {
                'some_key': {
                    'some_other_key': 'some_value',
                },
            },
        ],
        'NAME': 'some_db_name',
        'OPTIONS': {
            'init_command': 'SET foreign_key_checks = 0;',
        },
        'HOST': 'localhost',
        'USER': 'some_user_name',
        'PASSWORD': 'some_password',
        'PORT': '',
    },
}

如何在Django中获取用户IP地址?

问题:如何在Django中获取用户IP地址?

如何在Django中获取用户的IP?

我有这样的看法:

# Create your views
from django.contrib.gis.utils import GeoIP
from django.template import  RequestContext
from django.shortcuts import render_to_response


def home(request):
  g = GeoIP()
  client_ip = request.META['REMOTE_ADDR']
  lat,long = g.lat_lon(client_ip)
  return render_to_response('home_page_tmp.html',locals())

但是我得到这个错误:

KeyError at /mypage/
    'REMOTE_ADDR'
    Request Method: GET
    Request URL:    http://mywebsite.com/mypage/
    Django Version: 1.2.4
    Exception Type: KeyError
    Exception Value:    
    'REMOTE_ADDR'
    Exception Location: /mysite/homepage/views.py in home, line 9
    Python Executable:  /usr/bin/python
    Python Version: 2.6.6
    Python Path:    ['/mysite', '/usr/local/lib/python2.6/dist-packages/flup-1.0.2-py2.6.egg', '/usr/lib/python2.6', '/usr/lib/python2.6/plat-linux2', '/usr/lib/python2.6/lib-tk', '/usr/lib/python2.6/lib-old', '/usr/lib/python2.6/lib-dynload', '/usr/local/lib/python2.6/dist-packages', '/usr/lib/python2.6/dist-packages', '/usr/lib/pymodules/python2.6']
    Server time:    Sun, 2 Jan 2011 20:42:50 -0600

How do I get user’s IP in django?

I have a view like this:

# Create your views
from django.contrib.gis.utils import GeoIP
from django.template import  RequestContext
from django.shortcuts import render_to_response


def home(request):
  g = GeoIP()
  client_ip = request.META['REMOTE_ADDR']
  lat,long = g.lat_lon(client_ip)
  return render_to_response('home_page_tmp.html',locals())

But I get this error:

KeyError at /mypage/
    'REMOTE_ADDR'
    Request Method: GET
    Request URL:    http://mywebsite.com/mypage/
    Django Version: 1.2.4
    Exception Type: KeyError
    Exception Value:    
    'REMOTE_ADDR'
    Exception Location: /mysite/homepage/views.py in home, line 9
    Python Executable:  /usr/bin/python
    Python Version: 2.6.6
    Python Path:    ['/mysite', '/usr/local/lib/python2.6/dist-packages/flup-1.0.2-py2.6.egg', '/usr/lib/python2.6', '/usr/lib/python2.6/plat-linux2', '/usr/lib/python2.6/lib-tk', '/usr/lib/python2.6/lib-old', '/usr/lib/python2.6/lib-dynload', '/usr/local/lib/python2.6/dist-packages', '/usr/lib/python2.6/dist-packages', '/usr/lib/pymodules/python2.6']
    Server time:    Sun, 2 Jan 2011 20:42:50 -0600

回答 0

def get_client_ip(request):
    x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
    if x_forwarded_for:
        ip = x_forwarded_for.split(',')[0]
    else:
        ip = request.META.get('REMOTE_ADDR')
    return ip

确保正确配置了反向代理(如果有)(例如,mod_rpaf为Apache安装)。

注意:上面使用的第一X-Forwarded-For,但您可能要使用最后一项(例如,在Heroku的情况下:在Heroku上获取客户端的真实IP地址

然后将请求作为参数传递给它;

get_client_ip(request)
def get_client_ip(request):
    x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
    if x_forwarded_for:
        ip = x_forwarded_for.split(',')[0]
    else:
        ip = request.META.get('REMOTE_ADDR')
    return ip

Make sure you have reverse proxy (if any) configured correctly (e.g. mod_rpaf installed for Apache).

Note: the above uses the first item in X-Forwarded-For, but you might want to use the last item (e.g., in the case of Heroku: Get client’s real IP address on Heroku)

And then just pass the request as argument to it;

get_client_ip(request)

回答 1

您可以使用支持Python 23并处理IPv4IPv6的django-ipware

安装:

pip install django-ipware

简单用法:

# In a view or a middleware where the `request` object is available

from ipware import get_client_ip
ip, is_routable = get_client_ip(request)
if ip is None:
    # Unable to get the client's IP address
else:
    # We got the client's IP address
    if is_routable:
        # The client's IP address is publicly routable on the Internet
    else:
        # The client's IP address is private

# Order of precedence is (Public, Private, Loopback, None)

高级用法:

  • 自定义标头-ipware的自定义请求标头可以查看:

    i, r = get_client_ip(request, request_header_order=['X_FORWARDED_FOR'])
    i, r = get_client_ip(request, request_header_order=['X_FORWARDED_FOR', 'REMOTE_ADDR'])
  • 代理计数-Django服务器位于固定数量的代理之后:

    i, r = get_client_ip(request, proxy_count=1)
  • 受信任的代理-Django服务器位于一个或多个已知和受信任的代理之后:

    i, r = get_client_ip(request, proxy_trusted_ips=('177.2.2.2'))
    
    # For multiple proxies, simply add them to the list
    i, r = get_client_ip(request, proxy_trusted_ips=('177.2.2.2', '177.3.3.3'))
    
    # For proxies with fixed sub-domain and dynamic IP addresses, use partial pattern
    i, r = get_client_ip(request, proxy_trusted_ips=('177.2.', '177.3.'))

注意:请阅读此通知

You can use django-ipware which supports Python 2 & 3 and handles IPv4 & IPv6.

Install:

pip install django-ipware

Simple Usage:

# In a view or a middleware where the `request` object is available

from ipware import get_client_ip
ip, is_routable = get_client_ip(request)
if ip is None:
    # Unable to get the client's IP address
else:
    # We got the client's IP address
    if is_routable:
        # The client's IP address is publicly routable on the Internet
    else:
        # The client's IP address is private

# Order of precedence is (Public, Private, Loopback, None)

Advanced Usage:

  • Custom Header – Custom request header for ipware to look at:

    i, r = get_client_ip(request, request_header_order=['X_FORWARDED_FOR'])
    i, r = get_client_ip(request, request_header_order=['X_FORWARDED_FOR', 'REMOTE_ADDR'])
    
  • Proxy Count – Django server is behind a fixed number of proxies:

    i, r = get_client_ip(request, proxy_count=1)
    
  • Trusted Proxies – Django server is behind one or more known & trusted proxies:

    i, r = get_client_ip(request, proxy_trusted_ips=('177.2.2.2'))
    
    # For multiple proxies, simply add them to the list
    i, r = get_client_ip(request, proxy_trusted_ips=('177.2.2.2', '177.3.3.3'))
    
    # For proxies with fixed sub-domain and dynamic IP addresses, use partial pattern
    i, r = get_client_ip(request, proxy_trusted_ips=('177.2.', '177.3.'))
    

Note: read this notice.


回答 2

Alexander的回答很好,但是缺乏代理处理功能,这些代理有时会在HTTP_X_FORWARDED_FOR标头中返回多个IP。

真实IP通常位于列表的末尾,如此处所述:http : //en.wikipedia.org/wiki/X-Forwarded-For

该解决方案是对Alexander代码的简单修改:

def get_client_ip(request):
    x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
    if x_forwarded_for:
        ip = x_forwarded_for.split(',')[-1].strip()
    else:
        ip = request.META.get('REMOTE_ADDR')
    return ip

Alexander’s answer is great, but lacks the handling of proxies that sometimes return multiple IP’s in the HTTP_X_FORWARDED_FOR header.

The real IP is usually at the end of the list, as explained here: http://en.wikipedia.org/wiki/X-Forwarded-For

The solution is a simple modification of Alexander’s code:

def get_client_ip(request):
    x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
    if x_forwarded_for:
        ip = x_forwarded_for.split(',')[-1].strip()
    else:
        ip = request.META.get('REMOTE_ADDR')
    return ip

回答 3

我想提出对扬琴科答案的改进。

我不采用X_FORWARDED_FOR列表中的第一个IP,而是采用第一个不知道内部IP的IP,因为某些路由器不遵守该协议,您可以将内部IP视为列表的第一个值。

PRIVATE_IPS_PREFIX = ('10.', '172.', '192.', )

def get_client_ip(request):
    """get the client ip from the request
    """
    remote_address = request.META.get('REMOTE_ADDR')
    # set the default value of the ip to be the REMOTE_ADDR if available
    # else None
    ip = remote_address
    # try to get the first non-proxy ip (not a private ip) from the
    # HTTP_X_FORWARDED_FOR
    x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
    if x_forwarded_for:
        proxies = x_forwarded_for.split(',')
        # remove the private ips from the beginning
        while (len(proxies) > 0 and
                proxies[0].startswith(PRIVATE_IPS_PREFIX)):
            proxies.pop(0)
        # take the first ip which is not a private one (of a proxy)
        if len(proxies) > 0:
            ip = proxies[0]

    return ip

希望这对遇到同样问题的Google同事有所帮助。

I would like to suggest an improvement to yanchenko’s answer.

Instead of taking the first ip in the X_FORWARDED_FOR list, I take the first one which in not a known internal ip, as some routers don’t respect the protocol, and you can see internal ips as the first value of the list.

PRIVATE_IPS_PREFIX = ('10.', '172.', '192.', )

def get_client_ip(request):
    """get the client ip from the request
    """
    remote_address = request.META.get('REMOTE_ADDR')
    # set the default value of the ip to be the REMOTE_ADDR if available
    # else None
    ip = remote_address
    # try to get the first non-proxy ip (not a private ip) from the
    # HTTP_X_FORWARDED_FOR
    x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
    if x_forwarded_for:
        proxies = x_forwarded_for.split(',')
        # remove the private ips from the beginning
        while (len(proxies) > 0 and
                proxies[0].startswith(PRIVATE_IPS_PREFIX)):
            proxies.pop(0)
        # take the first ip which is not a private one (of a proxy)
        if len(proxies) > 0:
            ip = proxies[0]

    return ip

I hope this helps fellow Googlers who have the same problem.


回答 4

这是完成此任务的简短说明:

request.META.get('HTTP_X_FORWARDED_FOR', request.META.get('REMOTE_ADDR', '')).split(',')[0].strip()

here is a short one liner to accomplish this:

request.META.get('HTTP_X_FORWARDED_FOR', request.META.get('REMOTE_ADDR', '')).split(',')[0].strip()

回答 5

最简单的解决方案(如果您使用的是fastcgi + nignx)是itgorilla评论的内容:

谢谢您提出这个好问题。我的fastcgi没有传递REMOTE_ADDR元密钥。我在nginx.conf中添加了以下行,并修复了该问题:fastcgi_param REMOTE_ADDR $ remote_addr; – itgorilla

附:我添加这个答案只是为了使他的解决方案更明显。

The simpliest solution (in case you are using fastcgi+nignx) is what itgorilla commented:

Thank you for this great question. My fastcgi was not passing the REMOTE_ADDR meta key. I added the line below in the nginx.conf and fixed the problem: fastcgi_param REMOTE_ADDR $remote_addr; – itgorilla

Ps: I added this answer just to make his solution more visible.


回答 6

不再困惑在最新版本的Django中,明确提到客户端的Ip地址可从以下位置获得:

request.META.get("REMOTE_ADDR")

有关更多信息,请检查Django Docs

No More confusion In the recent versions of Django it is mentioned clearly that the Ip address of the client is available at

request.META.get("REMOTE_ADDR")

for more info check the Django Docs


回答 7

就我而言,上述方法均无效,因此我必须检查uwsgi+django源代码并在nginx中传递静态参数,并查看原因/方式,以下是我发现的内容。

环境信息:
python版本:2.7.5
Django版本:(1, 6, 6, 'final', 0)
nginx版本:nginx/1.6.0
uwsgi:2.0.7

环保设置信息:
nginx作为反向代理,在端口80 uwsgi处作为上游unix套接字侦听,最终将响应请求

Django配置信息:

USE_X_FORWARDED_HOST = True # with or without this line does not matter

Nginx的配置:

uwsgi_param      X-Real-IP              $remote_addr;
// uwsgi_param   X-Forwarded-For        $proxy_add_x_forwarded_for;
// uwsgi_param   HTTP_X_FORWARDED_FOR   $proxy_add_x_forwarded_for;

// hardcode for testing
uwsgi_param      X-Forwarded-For        "10.10.10.10";
uwsgi_param      HTTP_X_FORWARDED_FOR   "20.20.20.20";

在Django应用中获取所有参数:

X-Forwarded-For :       10.10.10.10
HTTP_X_FORWARDED_FOR :  20.20.20.20

结论:

因此,基本上,您必须在nginx中指定完全相同的字段/参数名称,并使用 request.META[field/param]在Django应用中。

现在,您可以决定是添加中间件(拦截器)还是仅HTTP_X_FORWARDED_FOR在某些视图中进行解析。

In my case none of above works, so I have to check uwsgi + django source code and pass static param in nginx and see why/how, and below is what I have found.

Env info:
python version: 2.7.5
Django version: (1, 6, 6, 'final', 0)
nginx version: nginx/1.6.0
uwsgi: 2.0.7

Env setting info:
nginx as reverse proxy listening at port 80 uwsgi as upstream unix socket, will response to the request eventually

Django config info:

USE_X_FORWARDED_HOST = True # with or without this line does not matter

nginx config:

uwsgi_param      X-Real-IP              $remote_addr;
// uwsgi_param   X-Forwarded-For        $proxy_add_x_forwarded_for;
// uwsgi_param   HTTP_X_FORWARDED_FOR   $proxy_add_x_forwarded_for;

// hardcode for testing
uwsgi_param      X-Forwarded-For        "10.10.10.10";
uwsgi_param      HTTP_X_FORWARDED_FOR   "20.20.20.20";

getting all the params in django app:

X-Forwarded-For :       10.10.10.10
HTTP_X_FORWARDED_FOR :  20.20.20.20

Conclusion:

So basically, you have to specify exactly the same field/param name in nginx, and use request.META[field/param] in django app.

And now you can decide whether to add a middleware (interceptor) or just parse HTTP_X_FORWARDED_FOR in certain views.


回答 8

最初从Django中删除功能的原因是最终无法信任标头。原因是容易欺骗。例如,建议的配置Nginx反向代理的方法是:

add_header X-Forwarded-For $proxy_add_x_forwarded_for;
add_header X-Real-Ip       $remote_addr;

当您这样做时:

curl -H 'X-Forwarded-For: 8.8.8.8, 192.168.1.2' http://192.168.1.3/

您在myhost.com中的nginx将继续发送:

X-Forwarded-For: 8.8.8.8, 192.168.1.2, 192.168.1.3

X-Real-IP如果您盲目按照说明进行操作它将是第一个先前的代理的IP。

如果信任您的用户是个问题,您可以尝试以下方法django-xffhttps : //pypi.python.org/pypi/django-xff/

The reason the functionality was removed from Django originally was that the header cannot ultimately be trusted. The reason is that it is easy to spoof. For example the recommended way to configure an nginx reverse proxy is to:

add_header X-Forwarded-For $proxy_add_x_forwarded_for;
add_header X-Real-Ip       $remote_addr;

When you do:

curl -H 'X-Forwarded-For: 8.8.8.8, 192.168.1.2' http://192.168.1.3/

Your nginx in myhost.com will send onwards:

X-Forwarded-For: 8.8.8.8, 192.168.1.2, 192.168.1.3

The X-Real-IP will be the IP of the first previous proxy if you follow the instructions blindly.

In case trusting who your users are is an issue, you could try something like django-xff: https://pypi.python.org/pypi/django-xff/


回答 9

我在上面的答案中也缺少代理。我get_ip_address_from_requestdjango_easy_timezones使用

from easy_timezones.utils import get_ip_address_from_request, is_valid_ip, is_local_ip
ip = get_ip_address_from_request(request)
try:
    if is_valid_ip(ip):
        geoip_record = IpRange.objects.by_ip(ip)
except IpRange.DoesNotExist:
    return None

这是method get_ip_address_from_request,IPv4和IPv6就绪的:

def get_ip_address_from_request(request):
    """ Makes the best attempt to get the client's real IP or return the loopback """
    PRIVATE_IPS_PREFIX = ('10.', '172.', '192.', '127.')
    ip_address = ''
    x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR', '')
    if x_forwarded_for and ',' not in x_forwarded_for:
        if not x_forwarded_for.startswith(PRIVATE_IPS_PREFIX) and is_valid_ip(x_forwarded_for):
            ip_address = x_forwarded_for.strip()
    else:
        ips = [ip.strip() for ip in x_forwarded_for.split(',')]
        for ip in ips:
            if ip.startswith(PRIVATE_IPS_PREFIX):
                continue
            elif not is_valid_ip(ip):
                continue
            else:
                ip_address = ip
                break
    if not ip_address:
        x_real_ip = request.META.get('HTTP_X_REAL_IP', '')
        if x_real_ip:
            if not x_real_ip.startswith(PRIVATE_IPS_PREFIX) and is_valid_ip(x_real_ip):
                ip_address = x_real_ip.strip()
    if not ip_address:
        remote_addr = request.META.get('REMOTE_ADDR', '')
        if remote_addr:
            if not remote_addr.startswith(PRIVATE_IPS_PREFIX) and is_valid_ip(remote_addr):
                ip_address = remote_addr.strip()
    if not ip_address:
        ip_address = '127.0.0.1'
    return ip_address

I was also missing proxy in above answer. I used get_ip_address_from_request from django_easy_timezones.

from easy_timezones.utils import get_ip_address_from_request, is_valid_ip, is_local_ip
ip = get_ip_address_from_request(request)
try:
    if is_valid_ip(ip):
        geoip_record = IpRange.objects.by_ip(ip)
except IpRange.DoesNotExist:
    return None

And here is method get_ip_address_from_request, IPv4 and IPv6 ready:

def get_ip_address_from_request(request):
    """ Makes the best attempt to get the client's real IP or return the loopback """
    PRIVATE_IPS_PREFIX = ('10.', '172.', '192.', '127.')
    ip_address = ''
    x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR', '')
    if x_forwarded_for and ',' not in x_forwarded_for:
        if not x_forwarded_for.startswith(PRIVATE_IPS_PREFIX) and is_valid_ip(x_forwarded_for):
            ip_address = x_forwarded_for.strip()
    else:
        ips = [ip.strip() for ip in x_forwarded_for.split(',')]
        for ip in ips:
            if ip.startswith(PRIVATE_IPS_PREFIX):
                continue
            elif not is_valid_ip(ip):
                continue
            else:
                ip_address = ip
                break
    if not ip_address:
        x_real_ip = request.META.get('HTTP_X_REAL_IP', '')
        if x_real_ip:
            if not x_real_ip.startswith(PRIVATE_IPS_PREFIX) and is_valid_ip(x_real_ip):
                ip_address = x_real_ip.strip()
    if not ip_address:
        remote_addr = request.META.get('REMOTE_ADDR', '')
        if remote_addr:
            if not remote_addr.startswith(PRIVATE_IPS_PREFIX) and is_valid_ip(remote_addr):
                ip_address = remote_addr.strip()
    if not ip_address:
        ip_address = '127.0.0.1'
    return ip_address

回答 10

在django.VERSION(2,1,1,’final’,0)请求处理程序中

sock=request._stream.stream.raw._sock
#<socket.socket fd=1236, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('192.168.1.111', 8000), raddr=('192.168.1.111', 64725)>
client_ip,port=sock.getpeername()

如果您两次调用上面的代码,您可能会得到

AttributeError(“’_ io.BytesIO’对象没有属性’stream’”,)

AttributeError(“’LimitedStream’对象没有属性’raw’”)

In django.VERSION (2, 1, 1, ‘final’, 0) request handler

sock=request._stream.stream.raw._sock
#<socket.socket fd=1236, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('192.168.1.111', 8000), raddr=('192.168.1.111', 64725)>
client_ip,port=sock.getpeername()

if you call above code twice,you may got

AttributeError(“‘_io.BytesIO’ object has no attribute ‘stream'”,)

AttributeError(“‘LimitedStream’ object has no attribute ‘raw'”)