鱼C论坛

 找回密码
 立即注册
楼主: lightninng

[技术交流] [记录贴]每天的问题和解决方法_正确学习方法

[复制链接]
发表于 2015-6-13 17:33:31 | 显示全部楼层
lightninng 发表于 2015-6-12 22:49
其实都是论坛或者checkio里面的一些小游戏 ,由简入繁,你也可以试试,由其是checkio这个,挺有趣的,我 ...

那你实在是太牛了,我要好好拜读一下才行~:lol:
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2015-6-13 18:00:40 | 显示全部楼层
本帖最后由 昨、夜星辰 于 2015-6-13 18:02 编辑

这网站真的很不错,居然还支持中文,是学习和巩固基础的好网站~就是慢了点~不知道是不是我的网络慢~
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2015-6-13 21:27:58 | 显示全部楼层
昨、夜星辰 发表于 2015-6-13 18:00
这网站真的很不错,居然还支持中文,是学习和巩固基础的好网站~就是慢了点~不知道是不是我的网络慢~

加油~~
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2015-6-13 22:23:56 | 显示全部楼层
[python cookbook]
问题:你有一个数据序列,想利用一些规则从中提取出需要的值或者是缩短序列
解决:使用列表推导生成器表达式

先看看列表推导:
  1. >>> mylist = [1, 4, -5, 10, -7, 2, 3, -1]
  2. >>> [n for n in mylist if n > 0]
  3. [1, 4, 10, 2, 3]
  4. >>> [n for n in mylist if n < 0]
  5. [-5, -7, -1]
复制代码
PS:相信稍微学习了一些python的鱼油对于列表推导都不陌生,这是很有python特色的一个工具,但是有一个问题,当列表很大时,我们使用列表推导会占用大量的内存,这个时候如果能有生成式就好,生成式每次用到下一个对象时即时计算而不提前计算好放在内存中,看下面的例子
  1. >>> pos = (n for n in mylist if n > 0)
  2. >>> pos
  3. <generator object <genexpr> at 0x1006a0eb0>
复制代码
PS:可以看到这里的pos是一个生成式,原来直接把方括号变成圆括号就可以得到生成式,这也是我看这节所学到的
另外,当遇到更复杂的情况时,你需要使用filter函数
  1. values = ['1', '2', '-3', '-', '4', 'N/A', '5']
  2. def is_int(val):
  3.     try:
  4.         x = int(val)
  5.         return True
  6.     except ValueError:
  7.         return False
  8. ivals = list(filter(is_int, values))
  9. print(ivals)
复制代码
输出是这样的
  1. >>>
  2. ['1', '2', '-3', '4', '5']
复制代码
另外 一个需要注意的是itertools模块中的compress函数(PS:吐槽一下,itertools里面的东西还真是好用啊),它以一个 iterable 对象和一个相对应的Boolean选择器序列作为输入参数。 然后输出 iterable 对象中对应选择器为True的元素。 当你需要用另外一个相关联的序列来过滤某个序列的时候,这个函数是非常有用的。比如下面的例子中想将那些对应count值大于5的地址全部输出
  1. addresses = [
  2.     '5412 N CLARK',
  3.     '5148 N CLARK',
  4.     '5800 E 58TH',
  5.     '2122 N CLARK'
  6.     '5645 N RAVENSWOOD',
  7.     '1060 W ADDISON',
  8.     '4801 N BROADWAY',
  9.     '1039 W GRANVILLE',
  10. ]
  11. counts = [ 0, 3, 10, 4, 1, 7, 6, 1]
复制代码
我们这样做
  1. >>> from itertools import compress
  2. >>> more5 = [n > 5 for n in counts]
  3. >>> more5
  4. [False, False, True, False, False, True, True, False]
  5. >>> list(compress(addresses, more5))
  6. ['5800 E 58TH', '4801 N BROADWAY', '1039 W GRANVILLE']
  7. >>>
复制代码
其实用filter函数重新定义一个函数也可以做到,不过这样做似乎更直观一些(PS:compress是压缩的意思,函数名起的真好啊)
最后,列表推导还有这样的用法
  1. >>> clip_neg = [n if n > 0 else 0 for n in mylist]
  2. >>> clip_neg
  3. [1, 4, 0, 10, 0, 2, 3, 0]
  4. >>> clip_pos = [n if n < 0 else 0 for n in mylist]
  5. >>> clip_pos
  6. [0, 0, -5, 0, -7, 0, 0, -1]
  7. >>>
复制代码
这其实是列表推导 和条件赋值组合使用的结果,条件赋值比如:
  1. >>> a = 10
  2. >>> b = 1 if a < 5 else 2
  3. >>> print(b)
  4. 2
  5. >>> a = 3
  6. >>> c = 1 if a < 5 else 2
  7. >>> c
  8. 1
复制代码
上面的例子根据a的大小是否小于5来决定b和c的赋值~~




想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2015-6-14 21:44:22 | 显示全部楼层
[python cookbook]
问题:你想构造一个字典,它是另外一个字典的子集。
解决:使用字典推导
PS:是的,你没有看错是字典推导,至少对于我来说是个新名词,只知道有列表推导,没想到字典也可以推导,来看例子
  1. prices = {
  2.     'ACME': 45.23,
  3.     'AAPL': 612.78,
  4.     'IBM': 205.55,
  5.     'HPQ': 37.20,
  6.     'FB': 10.75
  7. }
  8. # Make a dictionary of all prices over 200
  9. p1 = {key: value for key, value in prices.items() if value > 200}
  10. # Make a dictionary of tech stocks
  11. tech_names = {'AAPL', 'IBM', 'HPQ', 'MSFT'}
  12. p2 = {key: value for key, value in prices.items() if key in tech_names}
  13. print(p1)
  14. print(p2)
复制代码
输出如下
  1. >>> ================================ RESTART ================================
  2. >>>
  3. {'AAPL': 612.78, 'IBM': 205.55}
  4. {'AAPL': 612.78, 'HPQ': 37.2, 'IBM': 205.55}
复制代码
有些鱼油可能会想到,用键值对的元组做列表推导,然后将得到的列表作为字典创建时的传入参数可以达到同样的效果,如下面这样
  1. p1 = dict((key, value) for key, value in prices.items() if value > 200)
复制代码
那么两者的比较:1、字典推导更清晰,可读性好2、字典推导效率高(略高)
PS:关于效率上次了做lambda表达试和itemgetter,这次也来比一下
  1. from time import clock
  2. prices = {
  3. 'ACME': 45.23,
  4. 'AAPL': 612.78,
  5. 'IBM': 205.55,
  6. 'HPQ': 37.20,
  7. 'FB': 10.75
  8. }
  9. # Make a dictionary of all prices over 200

  10. clock_1 = clock()
  11. for i in range(1000000):
  12.     p1 = {key: value for key, value in prices.items() if value > 200}
  13. clock_2 = clock()
  14. for i in range(1000000):
  15.     p1 = dict((key, value) for key, value in prices.items() if value > 200)
  16. clock_3 = clock()
  17. print(clock_2-clock_1)
  18. print(clock_3-clock_2)
复制代码
结果是这样的
  1. >>> ================================ RESTART ================================
  2. >>>
  3. 1.1705024886121698
  4. 1.889318179922907
复制代码
可以看出效率还是差不少的,至少比上次做的效果要明显

有时候完成同一件事会有多种方式。比如,第二个例子程序也可以像这样重写:
  1. # Make a dictionary of tech stocks
  2. tech_names = { 'AAPL', 'IBM', 'HPQ', 'MSFT' }
  3. p2 = { key:prices[key] for key in prices.keys() & tech_names }
复制代码
但是,运行时间测试结果显示这种方案大概比第一种方案慢1.6倍。 如果对程序运行性能要求比较高的话,需要花点时间去做计时测试。PS:这个我没有测试,有兴趣的鱼油可以试一下~~

想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2015-6-14 21:58:02 | 显示全部楼层
楼主,我发现后面好多都是英文了~囧~
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2015-6-14 22:25:44 | 显示全部楼层
昨、夜星辰 发表于 2015-6-14 21:58
楼主,我发现后面好多都是英文了~囧~

每道题 都会给你一个example就是例子,会告诉你什么输入会得到什么输出
然后用百度翻译+example基本都能明白是怎么回事,实在看不懂的就略过~~
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2015-6-14 23:08:02 | 显示全部楼层
lightninng 发表于 2015-6-14 22:25
每道题 都会给你一个example就是例子,会告诉你什么输入会得到什么输出
然后用百度翻译+example基本都能 ...

嗯,有些题目我是靠看例子来做的,英文太烂了,机器翻译又乱七八糟的,囧~
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2015-6-15 22:27:17 | 显示全部楼层
昨、夜星辰 发表于 2015-6-14 23:08
嗯,有些题目我是靠看例子来做的,英文太烂了,机器翻译又乱七八糟的,囧~

恩。都这么过来的。学python英文多多少少是会接触的,就当锻炼英文吧,实在看不懂的略过先做其它的就行~~
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2015-6-15 22:48:31 | 显示全部楼层
[python cookbook]
问题:映射名称到序列元素
解决:运用命名元组collections模块中的namedtuple类型

PS:有些鱼油可能不明白这个是什么问题,稍微说一下,对于列表和元组来说,我们只能用下标来取某个值,但有时候每个值是有特殊的意义的,如一个存储了某个学生分数的元组(80,75,90),第一列是语文分数,第二列是数学分数,第三列是英语 分数,那么每次我们取语文分数时得得用下标0,以此类推,当科目比较多时,很难记住哪个科目在哪一个索引位置上。对于我来说,这种问题我想到的第一个办法是用一个字典来存储学生分数;另外一个方法是,用一个字典来存储索引位置。但是元组支持zip操作(大家应该记得之前有一节说到的用zip取某一列数据值吧,比如说这里要取所有学生的语文分数),还有就是解压操作(a,b,c = (80, 75,90)),等等~~
下面看一下例子
  1. >>> from collections import namedtuple
  2. >>> Subscriber = namedtuple('Subscriber', ['addr', 'joined'])
  3. >>> sub = Subscriber('jonesy@example.com', '2012-10-19')
  4. >>> sub
  5. Subscriber(addr='jonesy@example.com', joined='2012-10-19')
  6. >>> sub.addr
  7. 'jonesy@example.com'
  8. >>> sub.joined
  9. '2012-10-19'
  10. >>>
复制代码

来对比一下直接用元组和用namedtuple的代码的可读性(在写大程序的时候你经常需要去看自己的代码,除了注释,其它的比如变量命名,以及这种更直观的数据类型的应用都会影响程序的可读性):
  1. # 使用普通元组的代码
  2. def compute_cost(records):
  3. total = 0.0
  4. for rec in records:
  5.     total += rec[1] * rec[2]
  6. return total
复制代码

  1. # 使用nametuple的代码
  2. from collections import namedtuple

  3. Stock = namedtuple('Stock', ['name', 'shares', 'price'])
  4. def compute_cost(records):
  5.     total = 0.0
  6.     for rec in records:
  7.         s = Stock(*rec)
  8.     total += s.shares * s.price
  9.     return total
复制代码


另外 一个需要注意的问题是,元组的值是不可改变的,那么当我们想改变nametuple的某个值时怎么做呢,首先当然会想到平时我们想更改元组时做的,对了创建一个新的元组替代它,nametuple有个方法叫作_replace(PS:别忘了下划线),它可以返回一个新的nametuple,这个元组是将原元组中你指定的字段替换成新的值得到的。那么它有什么用呢,当某些时候你的数据并不完整有所缺失时,你可以先创建一个由缺省值组成的nametuple(这里我用命名为stock_prototype),在每次插入新的数据时,用这个变量的_replace方法将已知的字典更新进去,如下面这样
  1. from collections import namedtuple

  2. Stock = namedtuple('Stock', ['name', 'shares', 'price', 'date', 'time'])

  3. # Create a prototype instance
  4. stock_prototype = Stock('', 0, 0.0, None, None)

  5. # Function to convert a dictionary to a Stock
  6. def dict_to_stock(s):
  7.     return stock_prototype._replace(**s)
复制代码

下面是它的使用方法:
  1. >>> a = {'name': 'ACME', 'shares': 100, 'price': 123.45}
  2. >>> dict_to_stock(a)
  3. Stock(name='ACME', shares=100, price=123.45, date=None, time=None)
  4. >>> b = {'name': 'ACME', 'shares': 100, 'price': 123.45, 'date': '12/17/2012'}
  5. >>> dict_to_stock(b)
  6. Stock(name='ACME', shares=100, price=123.45, date='12/17/2012', time=None)
  7. >>>
复制代码

最后提示,当你创建的数据结构很复杂时,命名元组并不适合,这时候创建一个包含__slot__方法的类会更加的实用~~
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2015-6-17 23:11:41 | 显示全部楼层
[python cookbook]
问题:你需要在数据序列上执行聚集函数(比如 sum() , min() , max() ), 但是首先你需要先转换或者过滤数据
解决:使用一个生成器表达式参数

看例子
  1. nums = [1, 2, 3, 4, 5]
  2. s = sum(x * x for x in nums)
复制代码
PS:这里要计算nums中所有数的平方和,有些鱼油可能会选择这样的方式v
  1. s = sum([x * x for x in nums])
复制代码
那么两者有什么不同:下面的版本是将一个列表做为参数传入sum函数,我们知道sum函数可以接受一个可迭代对象或者多个可比较对象,下面的写法都是对的
  1. sum(0, 1, 2, 3)  # 传入多个比较对象
  2. sum(range(4))  # 传入1个可迭代对象
复制代码
还有一点,我自己以前犯过的错误,把一个可迭代对象和一个比较对象一起比较,结果告诉我两种对象不能比较,像下面这样
  1. >>> max([1, 2, 3, 4], 5)
  2. Traceback (most recent call last):
  3.   File "<pyshell#0>", line 1, in <module>
  4.     max([1, 2, 3, 4], 5)
  5. TypeError: unorderable types: int() > list()
复制代码

上面s的计算中,使用可生成式  x * x for x in nums  的好处是不用先在内存中创建一个列表[x * x for x in nums],从节省了内存,另外下面两种方法效果是差不多的
  1. s = sum(x * x for x in nums)
  2. s = sum((x * x for x in nums))
复制代码
第二种方法相当于传入一个可迭代对象,第一种方法传入多个比较对象,那么这三种方式效率都如何呢,我们来看看
  1. from time import clock
  2. clock()
  3. nums = list(range(100))
  4. first = clock()
  5. for i in range(1000000):
  6.     s = sum(x * x for x in nums)
  7. second = clock()
  8. for i in range(1000000):
  9.     s = sum((x * x for x in nums))
  10. third = clock()
  11. for i in range(1000000):
  12.     s = sum([x * x for x in nums])
  13. forth = clock()
  14. print(second-first)
  15. print(third-second)
  16. print(forth-third)
复制代码
以上是三种方式各运行100W次的用时对比,如果看下面~~
  1. >>>
  2. 8.507837522475626
  3. 8.30724140120892
  4. 6.948129495526043
  5. >>>
复制代码
可以看到前两种方式差别不算太大,,但第三种用列表的方式明显会快一 些,虽然不知道内部的机制,但是可以确定的是用列表的方式用内存换取了效率~~


想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2016-4-11 11:20:32 | 显示全部楼层
楼主那网页貌似打不开
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2016-4-11 11:27:10 | 显示全部楼层
感觉没鱼币什么都做不了
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2024-4-19 20:23

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表