记一次将长的列表推导改写的经过,请指正

148 views
Skip to first unread message

李海珍

unread,
Jul 9, 2014, 4:07:04 AM7/9/14
to pyth...@googlegroups.com
最近接手前同事负责的一个项目,
碰到一段比较复杂的代码,将其进行了改写,发上来,请大家评介指正一二。
原来代码如下:
 users = [ (lambda orgs, contact: {
                'type': contact.type if contact else 1,
                'uid': user.sid,
                'name': user.realname,
                'pinyin': json.dumps(pinyin_service.get_pinyin_searches_list(user.realname)),
                'level': contact.priority if contact else 0,
                'title': ','.join([ '{}-{}'.format(next((p.name for p in orgs if p.id==o.parent_id),None),o.name)
                                    for o in filter(lambda org:org.role == 3, orgs) ]),
                'department': ','.join([ o.name for o in filter(lambda org:org.role == 2, orgs) ]),
                "birthday": "", # TODO: birthday
                "avatar": "", # TODO: avatar
                'details': json.loads(contact.contacts) if contact else [],
                'groups': [ {
                    'id': o.id,
                    'type': 1,
                    'name': o.name,
                    'title': ' ', #TODO:
                } for o in filter(lambda org:org.role == 2, orgs) ]
            })
                  (organization_service.get_user_org_info(user.sid), contacts_service.get_by_uid(user.sid))
                  for user in user_service.get_users(key=g.key, type=2)
        ]

我花了好一段时间才理解,然后进行了改写:
我改写之后的代码如下:
users = []
for user in user_service.get_users(key=g.key, type=2):
    orgs = organization_service.get_user_org_info(user.sid) # 用户所在分组单位(可以有多个)
    contact = contacts_service.get_by_uid(user.sid)
    # role   # 1-单位 2-部门 3-职位
    positions = filter(lambda  org:org.role == 3, orgs)
    titles =  []
    for o in positions:  
        org_name = next((p.name for p in orgs if p.id == o.parent_id),'')
        titles.append('{}-{}'.format(org_name,o.name))
        
    title =  ','.join(titles)
    depts = filter(lambda  org:org.role == 2,orgs)
    dept_names = ','.join([ dept.name for dept in depts])
    groups = [{
        'id':o.id,
        'type':1,
        'name':o.name,
        'title':' ', #TODO
    } for o in depts ]
    r = {
        'type':contact.type if contact else 1,
        'uid':user.sid,
        'name':user.realname,
        'pinyin':json.dumps(pinyin_service.get_pinyin_searches_list(user.realname)),
        'level':contact.priority if contact else 0,
        'title':title,
        'department':dept_names,
        'birthday':'' ,#TODO:birthday
        'avatar':'', #TODO:AVATAR
        'details':json.loads(contact.contacts) if contact else [],
        'groups':groups
    }
    users.append(r)


大家觉得那一种易读,易维护一些。

我当然觉得我是的好一些,我是从C学起的,Python虽然接触好几年了,但是尚未真正入得门道。
C式风格流程式的写法,从上到下有组织的写好,我比较容易理解。
列表推导太长了,真的很让人难受,中间又加了那么些的表达式。

而且开头的,通过两个循环来得出 单位-职称  这一做法让人很觉得 奇怪。
怎么这个职位也变成一个orginazation来对待了,看来可能是设计有点不规范。


再者:代码还可以怎么样修改,还请一同指教。谢谢

凌肆

unread,
Jul 9, 2014, 4:47:33 AM7/9/14
to pyth...@googlegroups.com
首先呢,个人认为,列表推导和lambda是否难理解,和字多不多没关系,和格式写得好不好有关系。

另外, 其实你这个列表推导很短, 只有一层for。 里面套了个lambda而已,lambda其中也就都是一层的推导,拿出去之后也就剩下两三行。
其实你的前同事写得很明了啊。简洁有效。

什么叫长的列表推导。。 个人感觉类似这样

(python2)
something = {
    A: {
        B: C
        for B in a_list_dict[B_key]
        }
    for A, B_dict in my_set
    for B_key, C_list in B_dict.items()
    for C in C_list
}

以上算是一个简单版本的小case,也就是说,涉及到多个复杂的多层数据结构之间的交叉归并,结构转换,容易让脑袋里面栈溢出的,才叫做“长” 的列表推导,
一般这种列表推导都要求有严格的单元测试和在文档字符串中有准确的输入输出声明,否则没看懂的随便移动了一个for的位置就可能结构完全不一样,
至于把这种列表推导拆散成一般的for循环, 需要多声明一堆的中间变量,嵌套N层循环,造成比这还更难以理解。

你那个只是字比较多而已。。

如果你要提高之前代码的可读性, 可以给他加点注释,或者把变量名换得明确一点。
你改过后的代码比原先的运行效率要低得多。
--
--
邮件来自: `CPyUG`华蟒用户组(中文Python技术邮件列表)
规则: http://code.google.com/p/cpyug/wiki/PythonCn
发言: pyth...@googlegroups.com
详情: http://code.google.com/p/cpyug/wiki/CpyUg
G+: https://plus.google.com/u/0/communities/108786798869709602787
严正: 理解列表! 智慧提问! http://wiki.woodpecker.org.cn/moin/AskForHelp
---
您收到此邮件是因为您订阅了Google网上论坛中的“python-cn(华蟒用户组,CPyUG 邮件列表)”论坛。
要退订此论坛并停止接收此论坛的电子邮件,请发送电子邮件到python-cn+...@googlegroups.com
要在网络上查看此讨论,请访问https://groups.google.com/d/msgid/python-cn/6c1f938c-85d1-4878-890c-dff71d2ac919%40googlegroups.com
要查看更多选项,请访问https://groups.google.com/d/optout

Zephyr

unread,
Jul 9, 2014, 4:53:34 AM7/9/14
to pyth...@googlegroups.com

在 2014年7月9日 下午4:07,李海珍 <banx...@gmail.com>写道:
大家觉得那一种易读,易维护一些。

我当然觉得我是的好一些,我是从C学起的,Python虽然接触好几年了,但是尚未真正入得门道。
C式风格流程式的写法,从上到下有组织的写好,我比较容易理解。
列表推导太长了,真的很让人难受,中间又加了那么些的表达式。

而且开头的,通过两个循环来得出 单位-职称  这一做法让人很觉得 奇怪。
怎么这个职位也变成一个orginazation来对待了,看来可能是设计有点不规范。

再者:代码还可以怎么样修改,还请一同指教。谢谢


我个人的看法是觉得你改写后的好。复杂的东西(字符多也算),就用 for 规规矩矩地写。

如果要说改进,我建议把“直接数据”和“间接数据”分开。

比如,pinyin, title, department 等字段,还要折腾一遍的,算是“间接数据”。直接数据先填充好(这是一块代码),间接数据的处理算是另外一块代码(形式上分分就行),像 title, groups 这些好像还有些逻辑的,可以做到单独的函数中了。

另外,如果涉及外部服务的调用,"调用"的代码,还是单独成行比较好。

下面的代码不一定逻辑对哈。


res = user_service.get_users(key=g.key, type=1)
users = []

for u in res:
    p = {}
    p['uid'] = u.sid
    p['name'] = u.realname
    p['birthday'] = ''
    p['avatar'] = ''

    orgs = organization_service.get_user_org_info(u.sid)
    contact = contacts_service.get_by_uid(u.sid)
    pinyin = pinyin_service.get_pinyin_searches_list(u.realname)

    p['pinyin'] = json.dumps(pinyin)
    p['level'] = contact.priority if contact else 0
    p['details'] = json.loads(contact.contact) if contact else []

    p['title'] = format_title(orgs)
    p['department'] = format_dept(orgs)
    p['groups'] = format_groups(orgs)

    users.append(p)




--
进出自由才是游戏者的生存之道。

http://zouyesheng.com

Leo Jay

unread,
Jul 9, 2014, 4:54:45 AM7/9/14
to python-cn:CPyUG
如果是我,我会这样写:
ROLE_DEPT = 2
ROLE_POSITION = 3

DEFAULT_CONTACT_TYPE = 1
DEFAULT_CONTACT_PRIORITY = 0
DEFAULT_CONTACT_DETAIL = []

def get_pinyin(s):
return json.dumps(pinyin_service.get_pinyin_searches_list(s))

def find_org_by_id(orgs, id):
for org in orgs:
if org.id == id:
return org
return None

def get_position_title(position, orgs):
org = find_org_by_id(orgs, position.parent_id)
org_name = org.name if org else ''
return '{}-{}'.format(org_name, position.name)

def get_position_titles(positions, orgs):
return ','.join(get_position_title(pos, orgs) for pos in positions)

def get_user_dto(user):
orgs = organization_service.get_user_org_info(user.sid) # 用户所在分组单位(可以有多个)
contact = contacts_service.get_by_uid(user.sid)
if contact is None:
contact_type = DEFAULT_CONTACT_TYPE
contact_priority = DEFAULT_CONTACT_PRIORITY
contact_detail = DEFAULT_CONTACT_DETAIL
else:
contact_type = contact.type
contact_priority = contact.priority
contact_detail = json.loads(contact.contacts)
positions = [org for org in orgs if org.role == ROLE_POSITION]
depts = [org for org in orgs if org.role == ROLE_DEPT]
dept_names = ','.join(dept.name for dept in depts)
groups = [{
'id':dept.id,
'type':1,
'name':dept.name,
'title':' ', #TODO
} for dept in depts ]
return {
'type': contact_type,
'uid': user.sid,
'name': user.realname,
'pinyin': get_pinyin(user.realname),
'level': contact_priority,
'title': get_position_titles(positions, orgs),
'department': dept_names,
'birthday':'' ,#TODO:birthday
'avatar':'', #TODO:AVATAR
'details': contact_detail,
'groups': groups
}

users = [get_user_dto(user) for user in
user_service.get_users(key=g.key, type=2)]
> --
> --
> 邮件来自: `CPyUG`华蟒用户组(中文Python技术邮件列表)
> 规则: http://code.google.com/p/cpyug/wiki/PythonCn
> 发言: pyth...@googlegroups.com
> 详情: http://code.google.com/p/cpyug/wiki/CpyUg
> G+: https://plus.google.com/u/0/communities/108786798869709602787
> 严正: 理解列表! 智慧提问! http://wiki.woodpecker.org.cn/moin/AskForHelp
> ---
> 您收到此邮件是因为您订阅了Google网上论坛中的“python-cn(华蟒用户组,CPyUG 邮件列表)”论坛。
> 要退订此论坛并停止接收此论坛的电子邮件,请发送电子邮件到python-cn+...@googlegroups.com
> 要在网络上查看此讨论,请访问https://groups.google.com/d/msgid/python-cn/6c1f938c-85d1-4878-890c-dff71d2ac919%40googlegroups.com
> 要查看更多选项,请访问https://groups.google.com/d/optout



--
Best Regards,
Leo Jay

liuerfire Wang

unread,
Jul 9, 2014, 4:55:30 AM7/9/14
to pyth...@googlegroups.com
你同事写的才算Pythonic呀,对列表推导熟悉的话,那段代码很好懂。另外,一般来说,列表推倒的效率比append高哦。https://wiki.python.org/moin/PythonSpeed/PerformanceTips



2014-07-09 16:07 GMT+08:00 李海珍 <banx...@gmail.com>:

--
--
邮件来自: `CPyUG`华蟒用户组(中文Python技术邮件列表)
规则: http://code.google.com/p/cpyug/wiki/PythonCn
发言: pyth...@googlegroups.com
详情: http://code.google.com/p/cpyug/wiki/CpyUg
G+: https://plus.google.com/u/0/communities/108786798869709602787
严正: 理解列表! 智慧提问! http://wiki.woodpecker.org.cn/moin/AskForHelp
---
您收到此邮件是因为您订阅了Google网上论坛中的“python-cn(华蟒用户组,CPyUG 邮件列表)”论坛。
要退订此论坛并停止接收此论坛的电子邮件,请发送电子邮件到python-cn+...@googlegroups.com
要在网络上查看此讨论,请访问https://groups.google.com/d/msgid/python-cn/6c1f938c-85d1-4878-890c-dff71d2ac919%40googlegroups.com
要查看更多选项,请访问https://groups.google.com/d/optout



--
Best regards.
/**********************************
google+: +liuerfire twitter: @liuerfire
***********************************/

李海珍

unread,
Jul 9, 2014, 5:40:31 AM7/9/14
to pyth...@googlegroups.com
上面有两位同学说原来的代码好,
但是我这里有这个理由,不知道你们怎么在原来的代码上修改:
我接手这个代码的一个目的就是对输出的联系人进行过滤:
在我上面的代码中,我用下面的一行代码就OK了:
            if '离职' in dept_names or '退休' in dept_names:
                continue

但不知道在原来的代码中又将如何修改?

关于效率的话,列表推导带来的效率根本就不可观嘛。
而且在推导中for中做了下面两过数据库查询:
 (organization_service.get_user_org_info(user.sid), contacts_service.get_by_uid(user.sid))
在for里,有多个用户就得查询多少次? 要优化肯定得改写。

要说Pythonic的话,Pythonic我觉得也要易读,易修改吧!

依云

unread,
Jul 9, 2014, 5:45:55 AM7/9/14
to pyth...@googlegroups.com
我最喜欢这个版本了,清晰易读 =w=

PS: '{}-{}'.format 比 '%s-%s' % 的效率低,而它又比 a + '-' + b 的效率低
(假设 a 和 b 是字符串的话)。
> 您收到此邮件是因为您订阅了 Google 网上论坛的“python-cn(华蟒用户组,CPyUG 邮件列表)”论坛。
> 要退订此论坛并停止接收此论坛的电子邮件,请发送电子邮件到python-cn+...@googlegroups.com
> 要在网络上查看此讨论,请访问 https://groups.google.com/d/msgid/python-cn/CANMCpS5kZAzPK6T%2B6hmqZ-VmvJSHMz2nLnTNB7Q0ThukUxtFBw%40mail.gmail.com
> 要查看更多选项,请访问 https://groups.google.com/d/optout

--
Best regards,
lilydjwg

Linux Vim Python 我的博客:
http://lilydjwg.is-programmer.com/
--
A: Because it obfuscates the reading.
Q: Why is top posting so bad?

liuerfire Wang

unread,
Jul 9, 2014, 6:01:39 AM7/9/14
to pyth...@googlegroups.com
嗯,毕竟代码不是只有那一块,结合后面的需求,你同事的代码的确不太好。
P.S. Leo Jay的版本我也很喜欢,这种函数式的风格看起来很舒服^_^


--
--
邮件来自: `CPyUG`华蟒用户组(中文Python技术邮件列表)
规则: http://code.google.com/p/cpyug/wiki/PythonCn
发言: pyth...@googlegroups.com
详情: http://code.google.com/p/cpyug/wiki/CpyUg
G+: https://plus.google.com/u/0/communities/108786798869709602787
严正: 理解列表! 智慧提问! http://wiki.woodpecker.org.cn/moin/AskForHelp
---
您收到此邮件是因为您订阅了Google网上论坛中的“python-cn(华蟒用户组,CPyUG 邮件列表)”论坛。
要退订此论坛并停止接收此论坛的电子邮件,请发送电子邮件到python-cn+...@googlegroups.com

李海珍

unread,
Jul 9, 2014, 6:02:52 AM7/9/14
to pyth...@googlegroups.com
刚刚又对(organization_service.get_user_org_info(user.sid)方法j进行了一些性能优化。
根据前同事的原来的实现代码:
def _get_org_ids_by_uid(uid):
    return [ item.organization_id for item in db.query(OrganizationMember).filter(OrganizationMember.uid == uid).all()]

def get_user_org_info(uid):
    #return db.query(OrganizationMember).filter(OrganizationMember.uid == uid).first()
    ids = _get_org_ids_by_uid(uid)
    if ids:
        return db.query(Organization).filter(Organization.id.in_(ids)).all()
    else:
        return []


我想在一user的orgs_info的查询要两条SQL语句。
所以我想向下面这样优化(内存不是问题,整个数据库的大小不到1M,所以)
先将所有的数据,一次性请求出来,然后再用两个内部函数,然后改写如下:
        total_orgs = organization_service.get_all_orgs_by_key(g.key)
        total_org_members = organization_service.get_all_organization_members()

        def get_org_ids_by_uid(uid):
             return [ item.organization_id for item in total_org_members if item.uid == uid ]

        def get_user_org_info(uid):
            ids = get_org_ids_by_uid(uid)
            if ids:
                return [org for org in total_orgs if org.id in ids]
            else:
                return []


这么一改,效率有了比较大的提升,原来的方法,我多次请求测试为17秒左右。
修改之后,我多次请求时间大概在8秒左右。这真是显著提高啊。

接下来决定将,另一个方法: contacts_service.get_by_uid(user.sid)
也向上面的方法优化一下。

如果我不改下,估计不怎么好优化吧。
PS:数据库我用的是PostgreSQL,不太清楚怎么配置高速缓存(有知道的还希望指点一二)



在 2014年7月9日星期三UTC+8下午5时40分31秒,李海珍写道:

凌肆

unread,
Jul 9, 2014, 6:28:19 AM7/9/14
to pyth...@googlegroups.com
其实我觉得列表推导最直接的好处是:
代码看起来是从数据到数据的, 结构非常清晰明了。
这使得对于以数据为核心的编程变得非常方便和直观。
同时可以减少代码里面大部分无谓的中间变量,
如果其中涉及到一些复杂的判断,可以先把判断条件用函数提取出来之后放到for后面的if里。

月忧茗

unread,
Jul 9, 2014, 6:38:05 AM7/9/14
to pyth...@googlegroups.com
能轻松看懂的 才是好程序


要在网络上查看此讨论,请访问https://groups.google.com/d/msgid/python-cn/53BD1908.8030704%40gmail.com
要查看更多选项,请访问https://groups.google.com/d/optout



--

凌肆

unread,
Jul 9, 2014, 10:59:15 AM7/9/14
to pyth...@googlegroups.com
其实大多数人提到的,始终是列表推导的可读性问题。
楼主提到的数据库查询,条件判断这些,既然可以在forloop中优化,自然也可以在推导中优化。
比如提前将数据库拿到内存,都是一样的。

而对于可读性来说的话,看你怎么去理解这段程序了。
如果你把程序按照每一句的复杂度来理解,无疑forloop的可读性是最好的,逐行读下去都会明白这个程序在做什么。
但是程序的可读性的问题,并不是每次都面对的是没见过这段程序的人第一次阅读,更多的情况是有所了解的人对这段程序的回顾,排bug,更新升级。

而且实际上程序是在做什么,程序的实质始终是对数据以及数据结构的变化的推动,或者利用数据产生新的数据。
了解这段程序,或者整个模块的人,明显会对整个程序中流动的数据结构有所了解。
那么当他面对一个列表推导的时候,会最直观的判断出这一块数据的生成方式,而不是一个一个的去数层层嵌套的forloop中创造出来的无数中间变 量,甚至还要去趟以前因为中间变量命名不规范导致的大坑。
很明显数据才是程序的核心,而不是句子。
所以明确声明数据结构以及生成方式的列表推导,反而是可读性更高的,在越是复杂的数据结构转换中越是如此。(当然实在太多的时候还是建议对大的推 导中的某些较为独立的部分用函数进行包裹)
相反单层的列表推导,我倒是觉得写成哪样都无所谓,看起来都很简单。

yegle

unread,
Jul 9, 2014, 2:55:55 PM7/9/14
to pyth...@googlegroups.com
这不是一个典型的map/reduce吗…你把map function的逻辑写好,再复杂别人也能明白是一个map
function。写成for循环,再简单别人也得从for循环的头部读到底部才能理解你的意图。

看到代码里很多 ','.join([x for x in XXXX]),可以省略里面的方括号。
> 要在网络上查看此讨论,请访问https://groups.google.com/d/msgid/python-cn/53BD58AB.3040702%40gmail.com
> 要查看更多选项,请访问https://groups.google.com/d/optout



--
yegle
http://about.me/yegle

张裕

unread,
Jul 9, 2014, 9:51:19 PM7/9/14
to pyth...@googlegroups.com
我比较倾向于原程序,但是会把数据处理的逻辑写成明确的函数,如一个转换函数,一个过滤函数之类。


您收到此邮件是因为您订阅了 Google 网上论坛的“python-cn(华蟒用户组,CPyUG 邮件列表)”论坛。
要退订此论坛并停止接收此论坛的电子邮件,请发送电子邮件到python-cn+...@googlegroups.com
要在网络上查看此讨论,请访问 https://groups.google.com/d/msgid/python-cn/CAFL5w3UtaUXqwDyvkTqhR5R1rohA24hCGxMRAuP1nXM_JG-DGw%40mail.gmail.com
要查看更多选项,请访问 https://groups.google.com/d/optout



--
Gentoo, DIY your desktop

shhgs

unread,
Jul 9, 2014, 9:59:54 PM7/9/14
to pyth...@googlegroups.com
我选择原来的版本,但是这个lambda太违和了。


Shell Xu

unread,
Jul 9, 2014, 10:02:11 PM7/9/14
to CUPG
类似代码总共有三种写法:
1. 列表推导
2. for循环
3. 函数映射
三种写法还可以交叉,组合起来会非常复杂。

一般建议如下:

如果过程是严密的函数映射过程,函数映射总是最好懂的。因为函数映射组合中,每个函数的作用都是固定的。map调用的函数只能映射,filter的只能过滤,reduce的只能——呃,reduce。所以即使多个函数对对象反复操作,每个部分也很容易理解。但是同样的,函数映射也是最死板的。同样的中间变量,在map中求值过了,reduce中还得另行求值——除非你使用对象传递。

而for循环是最不好懂的,因为for循环的边界效用太高。你可以在for循环里面反复使用上一轮迭代的中间值进行累加递推,还可以使用直接取元素的方法操纵非生成器的对象,获得前趋和后置元素。甚至可以利用这种效应直接做算法。就是因为for的灵活性,所以如果在代码里面看到一个for,而这个for不能显然的变形为函数映射(变量传递)。那么我第一反应都会比较头痛。

列表推导相比起来,近乎介于两者之间。既不像函数那样严密,又不像for那么灵活。一般来说,用来写短小句子非常合适。用在长句子上总有点这种感觉:

[i for i in x if (True if i > a else (False if i < b else i % c))]

这就像小说里面的超长句子一样,是一种把人话合法的说成人听不懂的效果。。。


在 2014年7月9日 下午4:07,李海珍 <banx...@gmail.com>写道:

--
--
邮件来自: `CPyUG`华蟒用户组(中文Python技术邮件列表)
规则: http://code.google.com/p/cpyug/wiki/PythonCn
发言: pyth...@googlegroups.com
详情: http://code.google.com/p/cpyug/wiki/CpyUg
G+: https://plus.google.com/u/0/communities/108786798869709602787
严正: 理解列表! 智慧提问! http://wiki.woodpecker.org.cn/moin/AskForHelp
---
您收到此邮件是因为您订阅了Google网上论坛中的“python-cn(华蟒用户组,CPyUG 邮件列表)”论坛。
要退订此论坛并停止接收此论坛的电子邮件,请发送电子邮件到python-cn+...@googlegroups.com



--
彼節者有間,而刀刃者無厚;以無厚入有間,恢恢乎其於游刃必有餘地矣。
blog: http://shell909090.org/blog/

yi huang

unread,
Jul 9, 2014, 10:47:47 PM7/9/14
to python-cn
关键在于,那个lambda应该拿出去,成为一个函数的。然后列表推导就好看了。

2014-07-09 16:07 GMT+08:00 李海珍 <banx...@gmail.com>:
> --
> --
> 邮件来自: `CPyUG`华蟒用户组(中文Python技术邮件列表)
> 规则: http://code.google.com/p/cpyug/wiki/PythonCn
> 发言: pyth...@googlegroups.com
> 详情: http://code.google.com/p/cpyug/wiki/CpyUg
> G+: https://plus.google.com/u/0/communities/108786798869709602787
> 严正: 理解列表! 智慧提问! http://wiki.woodpecker.org.cn/moin/AskForHelp
> ---
> 您收到此邮件是因为您订阅了Google网上论坛中的“python-cn(华蟒用户组,CPyUG 邮件列表)”论坛。
> 要退订此论坛并停止接收此论坛的电子邮件,请发送电子邮件到python-cn+...@googlegroups.com
> 要在网络上查看此讨论,请访问https://groups.google.com/d/msgid/python-cn/6c1f938c-85d1-4878-890c-dff71d2ac919%40googlegroups.com
> 要查看更多选项,请访问https://groups.google.com/d/optout



--
http://yi-programmer.com/

Pison

unread,
Jul 10, 2014, 2:15:15 AM7/10/14
to pyth...@googlegroups.com
有没有考虑过这样写的函数调用开销?——我不推荐
> 要退订此论坛并停止接收此论坛的电子邮件,请发送电子邮件到python-cn+unsubscribe@googlegroups.com

Pison

unread,
Jul 10, 2014, 2:18:48 AM7/10/14
to pyth...@googlegroups.com
赞同你的观点。  楼主的做法是传统的沿用,并没有很好的继承python。谈到易读性,容我猜测楼主对python的一些惯常用法不够熟悉——不过我也不推荐前同事的做法,虽然list的层级很小,但是内容长度过长, 看起来也不舒服——容易吓到新人。 

当然,楼主的写法,并非有错,除了效率差一点点点外。
要退订此论坛并停止接收此论坛的电子邮件,请发送电子邮件到python-cn+unsubscribe@googlegroups.com

Pison

unread,
Jul 10, 2014, 2:21:55 AM7/10/14
to pyth...@googlegroups.com
话说回来,map 和filter 这类函数,Python 并不推荐。 理由据说是不易理解——对新人。
>> 要退订此论坛并停止接收此论坛的电子邮件,请发送电子邮件到python-cn+unsubscribe@googlegroups.com
>>
>> 要在网络上查看此讨论,请访问https://groups.google.com/d/msgid/python-cn/aba54af1-c338-4adf-accc-5e031a40de16%40googlegroups.com
>> 要查看更多选项,请访问https://groups.google.com/d/optout
>>
>>
>> --
>> --
>> 邮件来自: `CPyUG`华蟒用户组(中文Python技术邮件列表)
>> 规则: http://code.google.com/p/cpyug/wiki/PythonCn
>> 发言: pyth...@googlegroups.com
>> 详情: http://code.google.com/p/cpyug/wiki/CpyUg
>> G+: https://plus.google.com/u/0/communities/108786798869709602787
>> 严正: 理解列表! 智慧提问! http://wiki.woodpecker.org.cn/moin/AskForHelp
>> ---
>> 您收到此邮件是因为您订阅了Google网上论坛中的“python-cn(华蟒用户组,CPyUG 邮件列表)”论坛。
>> 要退订此论坛并停止接收此论坛的电子邮件,请发送电子邮件到python-cn+unsubscribe@googlegroups.com
>>
>> 要在网络上查看此讨论,请访问https://groups.google.com/d/msgid/python-cn/53BD1908.8030704%40gmail.com
>> 要查看更多选项,请访问https://groups.google.com/d/optout
>
>
>
>
> --
> My GitHub
> https://github.com/yueyoum
>
> --
> --
> 邮件来自: `CPyUG`华蟒用户组(中文Python技术邮件列表)
> 规则: http://code.google.com/p/cpyug/wiki/PythonCn
> 发言: pyth...@googlegroups.com
> 详情: http://code.google.com/p/cpyug/wiki/CpyUg
> G+: https://plus.google.com/u/0/communities/108786798869709602787
> 严正: 理解列表! 智慧提问! http://wiki.woodpecker.org.cn/moin/AskForHelp
> ---
> 您收到此邮件是因为您订阅了Google网上论坛中的“python-cn(华蟒用户组,CPyUG 邮件列表)”论坛。
> 要退订此论坛并停止接收此论坛的电子邮件,请发送电子邮件到python-cn+unsubscribe@googlegroups.com
> 要在网络上查看此讨论,请访问https://groups.google.com/d/msgid/python-cn/CALZWPGiieOz_WqewOmxTMesRcWG1Th17qvn%3D%3Dg4VXz%3DFQK%3DXdw%40mail.gmail.com
> 要查看更多选项,请访问https://groups.google.com/d/optout
>
>
> --
> --
> 邮件来自: `CPyUG`华蟒用户组(中文Python技术邮件列表)
> 规则: http://code.google.com/p/cpyug/wiki/PythonCn
> 发言: pyth...@googlegroups.com
> 详情: http://code.google.com/p/cpyug/wiki/CpyUg
> G+: https://plus.google.com/u/0/communities/108786798869709602787
> 严正: 理解列表! 智慧提问! http://wiki.woodpecker.org.cn/moin/AskForHelp
> ---
> 您收到此邮件是因为您订阅了Google网上论坛中的“python-cn(华蟒用户组,CPyUG 邮件列表)”论坛。
> 要退订此论坛并停止接收此论坛的电子邮件,请发送电子邮件到python-cn+unsubscribe@googlegroups.com

Pison

unread,
Jul 10, 2014, 2:36:48 AM7/10/14
to pyth...@googlegroups.com
将members全部取出,是否需要考虑数据库的成长?……现在提速是因为楼主修改,少了一层循环(直接获取表内所有数据)。 随着数据库增长,外面这层的循环也需要考虑吧?

这用的是sqlalchemy 吧?  Django 中有个函数values_list 可以将 models某几个值列出到list中。 
如 Model.objects.all().values_list('user_id', flat=True) 返回 ['1', '2', '3', '4' ...]
sqlalchemy应该也有类似的函数吧

李海珍

unread,
Jul 10, 2014, 3:37:20 AM7/10/14
to pyth...@googlegroups.com

你说的对,应该只取需要的值。SQL Alchemy 也有这样的用法,谢谢提醒。一时之间竟忘了,这样优化。

lee Alexander

unread,
Jul 12, 2014, 2:20:37 PM7/12/14
to pyth...@googlegroups.com
[i for i in x if (True if i > a else (False if i < b else i % c))]   这样的不似人话的例子主要是一些一行代码怎么怎么样的脑洞题搞出来,变量定义过于随意造成的,替换成for也会头痛。且列表解析格式化一下就容易看明白了

[i
 for i in x 
if (True if i > a else (False if i < b else i % c))
]

分行出来一看复杂的还是这个if的条件,那么放在for里嵌套if一样的难懂


我觉得楼主的命令式编程的思维过于根深蒂固才会觉得列表推导难以理解吧


Yunfan Jiang

unread,
Jul 13, 2014, 1:09:06 AM7/13/14
to pyth...@googlegroups.com
对 这正是我想说的
原代码的推导只有一层 没什么大不了的 我经常写两三层的

之所以作者觉得不好理解 我个人觉得是那个lambda在里面占据了大量版面 
所以最好抽出去 而且后面有修改也容易 并且如果过滤函数名起得得当 会更容易理解整段代码的意图

我觉得列表推导比较好 可以一行表达一个逻辑 而不是for那种 要结合不少行代码才能猜出意图来





--
Name: yunfan
Site: http://geek42.info/
Interest:
  - Lang: [forth, clojure, c, python, lua]
  - software: [nginx, redis]
  - abstract: [vm, tiny, cloud, html5]
  - history
  - science-fiction
  - music: [new-age, vangelis, yanni]

Shell Xu

unread,
Jul 13, 2014, 3:56:31 AM7/13/14
to CPUG

来个恶心人的。
lambda f :(dict(((k, v[1:]) for k, v in m.iteritems() if f(v[0]))) if hasattr(m, '__getitem__') else ([v[1:] for v in m if f(v[0])] if hasattr(m, '__iter__') else v[1:]))(lambda x: ord(x) < 60)

手机写程序累死了,帮我看看有没有错。。。

from nexus 4

董诣

unread,
Jul 13, 2014, 10:35:06 PM7/13/14
to pyth...@googlegroups.com
list comperhension 其实就是sql
[i.xx for oo in xxoo if oo.aa = bb] 

select xx from xxoo where aa=bb


Zoom.Quiet

unread,
Jul 13, 2014, 11:24:56 PM7/13/14
to CPyUG~华蠎用户组
2014-07-14 10:34 GMT+08:00 董诣 <juv...@gmail.com>:
>
> list comperhension 其实就是sql

对的,这个思路才对,
所以,应该找找对应的模块是否有专用的,
或是提升推导表述能力的,,,生这么写,无法将 Py 变成 Lisp 的
> --
> --
> 邮件来自: `CPyUG`华蟒用户组(中文Python技术邮件列表)
> 规则: http://code.google.com/p/cpyug/wiki/PythonCn
> 发言: pyth...@googlegroups.com
> 详情: http://code.google.com/p/cpyug/wiki/CpyUg
> G+: https://plus.google.com/u/0/communities/108786798869709602787
> 严正: 理解列表! 智慧提问! http://wiki.woodpecker.org.cn/moin/AskForHelp
> ---
> 您收到此邮件是因为您订阅了Google网上论坛中的“python-cn(华蟒用户组,CPyUG 邮件列表)”论坛。
> 要退订此论坛并停止接收此论坛的电子邮件,请发送电子邮件到python-cn+...@googlegroups.com
> 要在网络上查看此讨论,请访问https://groups.google.com/d/msgid/python-cn/CAM2JP_W5zT-vQpoTKmjAmZBGiDNOOotWTsYxME3Ffr0k1g5_yQ%40mail.gmail.com
> 要查看更多选项,请访问https://groups.google.com/d/optout




--
人生苦短, Pythonic! 冗余不做,日子甭过!备份不做,十恶不赦!
KM keep growing environment culture which promoting organization be learnning!
俺: http://zoomquiet.io
许: http://creativecommons.org/licenses/by-sa/2.5/cn/

凌肆

unread,
Jul 14, 2014, 5:31:07 AM7/14/14
to pyth...@googlegroups.com
这个的恶心之处不在于代码,在于格式。。

lambda f :(
    dict(

        ((k, v[1:]) for k, v in m.iteritems() if f(v[0]))
    )
    if hasattr(m, '__getitem__')
    else (
        [v[1:] for v in m if f(v[0])]
        if hasattr(m, '__iter__')
        else v[1:]
    )
)(lambda x: ord(x) < 60)

额, 这是把一个lambda表达式作为值传入??

就像lisp一样, 虽然可以不换行缩进, 但是不换行缩进就是天书。。


On 2014年07月13日 15:56, Shell Xu wrote:

来个恶心人的。
lambda f :(dict(((k, v[1:]) for k, v in m.iteritems() if f(v[0]))) if hasattr(m, '__getitem__') else ([v[1:] for v in m if f(v[0])] if hasattr(m, '__iter__') else v[1:]))(lambda x: ord(x) < 60)

手机写程序累死了,帮我看看有没有错。。。

from nexus 4

On Jul 13, 2014 2:20 AM, "lee Alexander" <superp...@gmail.com> wrote:
[i for i in x if (True if i > a else (False if i < b else i % c))]   这样的不似人话的例子主要是一些一行代码怎么怎么样的脑洞题搞出来,变量定义过于随意造成的,替换成for也会头痛。且列表解析格式化一下就容易看明白了

[i
 for i in x 
if (True if i > a else (False if i < b else i % c))
]

分行出来一看复杂的还是这个if的条件,那么放在for里嵌套if 一样的难懂


我觉得楼主的命令式编程的思维过于根深蒂固才会觉得列表推导难以理 解吧
在 2014年7月10日 上午10:01,Shell Xu <shell...@gmail.com>写 道:
类似代码总共有三种写法:
1. 列表推导
2. for循环
3. 函数映射
三种写法还可以交叉,组合起来会非常复杂。

一般建议如下:

如果过程是严密的函数映射过程,函数映射总是最好懂的。因为函数映射组合中,每个函数的作用都是固定的。 map调用的函数只能映射,filter的只能过滤,reduce的只能——呃,reduce。所以即使多个函 数对对象反复操作,每个部分也很容易理解。但是同样的,函数映射也是最死板的。同样的中间变量,在map中求值 过了,reduce中还得另行求值——除非你使用对象传递。

而for循环是最不好懂的,因为for循环的边界效用太高。你可以在for循环里面反复使用上一轮迭代的 中间值进行累加递推,还可以使用直接取元素的方法操纵非生成器的对象,获得前趋和后置元素。甚至可以利用这种效 应直接做算法。就是因为for的灵活性,所以如果在代码里面看到一个for,而这个for不能显然的变形为函数 映射(变量传递)。那么我第一反应都会比较头痛。

列表推导相比起来,近乎介于两者之间。既不像函数那样严密,又不像for那么灵活。一般来说,用来写短小句子非 常合适。用在长句子上总有点这种感觉:
您收到此邮件是因为您订阅了Google网上论坛中的“python-cn(华蟒用户 组,CPyUG 邮件列表)”论坛。



--

Shell Xu

unread,
Jul 15, 2014, 5:22:01 AM7/15/14
to CUPG
所以,这段代码试图做什么呢?


要在网络上查看此讨论,请访问https://groups.google.com/d/msgid/python-cn/53C3A34D.3060904%40gmail.com
要查看更多选项,请访问https://groups.google.com/d/optout

Wang Xuerui

unread,
Jul 15, 2014, 5:39:09 AM7/15/14
to pyth...@googlegroups.com
在 2014年7月15日 下午5:21,Shell Xu <shell...@gmail.com>写道:
>
> 所以,这段代码试图做什么呢?
>
>
> 在 2014年7月14日 下午5:30,凌肆 <tvb...@gmail.com>写道:
>
>> 这个的恶心之处不在于代码,在于格式。。
>> 。
>> lambda f :(
>> dict(
>>
>> ((k, v[1:]) for k, v in m.iteritems() if f(v[0]))
>> )
>> if hasattr(m, '__getitem__')
>> else (
>> [v[1:] for v in m if f(v[0])]
>> if hasattr(m, '__iter__')
>> else v[1:]
>> )
>> )(lambda x: ord(x) < 60)
>>
>> 额, 这是把一个lambda表达式作为值传入??
>>
>> 就像lisp一样, 虽然可以不换行缩进, 但是不换行缩进就是天书。。
>>
>>
>> On 2014年07月13日 15:56, Shell Xu wrote:
>>
>> 来个恶心人的。
>> lambda f :(dict(((k, v[1:]) for k, v in m.iteritems() if f(v[0]))) if hasattr(m, '__getitem__') else ([v[1:] for v in m if f(v[0])] if hasattr(m, '__iter__') else v[1:]))(lambda x: ord(x) < 60)
>>
>> 手机写程序累死了,帮我看看有没有错。。。

两组a if cond else b的条件表达式... lambda里面是

{k: v[1:] for k, v in m.iteritems() if f(v[0])} # 写成dict(((k, v)
for k, v in xxx))绝对是要报复社会的吧- -||
if hasattr(m, '__getitem__')
else (
[v[1:] for v in m if f(v[0])]
if hasattr(m, '__iter__')
else v[1:]
)

f是判断x字符序数是否在60之前的, dict comprehension是说对m中的每一对k,
v如果v的第一个字符(元素)满足f那么结果中包含一对k: v[1:]... list
comprehension是说对m中的每个元素如果其第一个字符(元素)满足f那么结果中包含一个v[1:]... 两个判断条件是 "类型检查"
用来选择合适的语法 (第一个条件不太对其实, 因为显然实现了__getitem__界面的类不一定有iteritems实现,
虽然一般都会配一个... __iter__那个倒是正确的鸭子类型使用). 如果m既没有dict界面又没有iterable界面的话,
就直接slice掉第一个字符...

那么这段程序的大概意思就是把m容器中的元素 *值* 第一个字符/元素小于60的K/V对或者元素挑出来, 同时把这个第一个字符/元素切掉,
至少适用实现了__getitem__和iteritems的dict-like界面和iterable界面...
感觉不是随手写的报复社会代码就是什么业务逻辑代码直接抽出来的样子= =

Shell Xu

unread,
Jul 15, 2014, 6:17:24 AM7/15/14
to CUPG
这段代码其实逻辑很简单。过滤dict/list类容器的值,选出第一个字符序数小于60的,并把第一个字符切掉。
写成dict(...)是因为对老版本的兼容性。
__getitem__是必须的,因为没有__getitem__,就不能当dict容器了。即使有iteritems也没用。这种对象我宁可直接在v[1:]上报错。如果有了__getitem__却没有iteritems,这种对象我也没法下嘴啊。总不能iter完了再__getitem__吧。这点没做到我认为是dict类对象实现的错。当然,稳妥起见一般配的都是items,这个更加合理。
这个语句的恶心之处绝对不是排版,而是出了错没法调。当一句错误提示提到这句的时候,鬼才知道问题在哪个子句。而且连调试语句都没地方插。。。

BTW:这个其实是随手写报复社会,不过代码是有原型的。


--
--
邮件来自: `CPyUG`华蟒用户组(中文Python技术邮件列表)
规则: http://code.google.com/p/cpyug/wiki/PythonCn
发言: pyth...@googlegroups.com
详情: http://code.google.com/p/cpyug/wiki/CpyUg
G+: https://plus.google.com/u/0/communities/108786798869709602787
严正: 理解列表! 智慧提问! http://wiki.woodpecker.org.cn/moin/AskForHelp
---
您收到此邮件是因为您订阅了 Google 网上论坛的“python-cn(华蟒用户组,CPyUG 邮件列表)”论坛。
要退订此论坛并停止接收此论坛的电子邮件,请发送电子邮件到python-cn+...@googlegroups.com
要在网络上查看此讨论,请访问 https://groups.google.com/d/msgid/python-cn/CABK5mSb0mdMhWJ4yJRD%3DOXo71tDYOiLi%2BfOiMimE4Q3atpk0BQ%40mail.gmail.com
要查看更多选项,请访问 https://groups.google.com/d/optout

whycrying

unread,
Jul 15, 2014, 10:20:08 AM7/15/14
to pyth...@googlegroups.com
> ...

> 这个语句的恶心之处绝对不是排版,而是出了错没法调。当一句错误提示提到这句的时候,鬼才知道问题在哪个子句。而且连调试语句都没地方插。。。
> ...

强烈赞同!

短小简单易懂的代码才是正道啊!


2014-07-15 18:16 GMT+08:00 Shell Xu <shell...@gmail.com>:
这段代码其实逻辑很简单。过滤dict/list类容器的值,选出第一个字符序数小于60的,并把第一个字符切掉。
写成dict(...)是因为对老版本的兼容性。
__getitem__是必须的,因为没有__getitem__,就不能当dict容器了。即使有iteritems也没用。这种对象我宁可直接在v[1:]上报错。如果有了__getitem__却没有iteritems,这种对象我也没法下嘴啊。总不能iter完了再__getitem__吧。这点没做到我认为是dict类对象实现的错。当然,稳妥起见一般配的都是items,这个更加合理。
这个语句的恶心之处绝对不是排版,而是出了错没法调。当一句错误提示提到这句的时候,鬼才知道问题在哪个子句。而且连调试语句都没地方插。。。

BTW:这个其实是随手写报复社会,不过代码是有原型的。

Peng Dai

unread,
Jul 15, 2014, 11:27:36 PM7/15/14
to pyth...@googlegroups.com
我比较倾向 @Leo Jay  的方法,这段组合短小函数的写法, 易读性和可维护行都不错。 不过太多函数调用也是比较吃性能的,见仁见智吧。

例外对于 @Leo Jay 的说法也比较认同, 你那个推导外层不复杂,比较麻烦的就是中间那段lambda, 可以考虑提出来写成个函数调用。这样才比较pythonic
Reply all
Reply to author
Forward
0 new messages