date()
可以将时间戳格式化成字符串,最常用的就是YYYY-mm-dd HH:ii:ss
这种格式了。可以不加$timestamp
参数,默认是当前时间。
1
|
|
时间一般都会要前导零,记住这几个格式化占位符就不用查手册了。
这个函数其实感觉蛮诡异的……因为它可以尝试把美国英语日期格式的字符串解释成时间戳,可能会失败,所以感觉还是不要传比较复杂的字符串给它比较好……
这是官方文档给的例子
1 2 3 4 5 6 7 |
|
在不知道有”-1 day”这种用法的时候我都是自己实现的……所以还是熟悉PHP的函数比较好。
在程序入口处设置时区
1
|
|
明早就要做火车去深圳面试了,开心 =w=
]]>项目需要作为客户端发起HTTP请求,查一些API来分析一些数据,于是就接触了cURL这个库。作为天生的web语言,PHP在这方面真是太厉害了,居然自带了HTTP请求的函数。除了cURL外,file_get_contents
这个函数也可以发起HTTP请求,不过不是专门干这个的,不好配置参数,还是cURL好。
官方文档里的例子很有用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
主要就是对curl的选项进行配置,即curl_setopt()
这个函数。有几个参数很重要:
CURLOPT_URL
:url,没得说CURLOPT_RETURNTRANSFER
:设为1才会将响应结果存到变量,否则会echo到页面CURLOPT_HTTPHEADER
:设置一个HTTP请求头,必须为array
,否则会报错CURLOPT_TIMEOUT
:设置cURL执行超时长度,单位是秒,注意是执行时间,而不是连接时间,那要用CURLOPT_CONNECTTIMEOUT
CURLOPT_TIMEOUT_MS
:同上,毫秒更多的配置查手册吧。
有时候执行完curl_exec()
会发现没有任何东西返回,这时候需要curl_errno()
和curl_error()
来查看错误号和错误原因了,不过一定要放在curl_exec()
后面,不要像我一样蠢……(躲角落哭)
1 2 3 4 5 6 7 8 9 10 11 12 |
|
别忘了PHP才是最好的语言!
参考资料
[1] Client URL Library
[2] Setting Curl’s Timeout in PHP
InnoDB设计目标是处理大容量的数据,而MyISAM追求的是性能,两者产生的差异也是基于这点。 InnoDB是MySQL的默认存储引擎。
InnoDB支持事务和外键,MyISAM不支持。MyISAM强调的是性能,InnoDB支持的功能更加完整。InnoDB支持事务带来了一个好处,发生故障时可以通过事务日志来恢复数据库,MyISAM特别要命的一点是崩溃后不能安全恢复,所以对于表比较大的情况不要用。
两种存储引擎的效率差异来自于锁的方式差异,MyISAM是表锁,对数据库进行写操作时会锁住整个表,效率很低;确定要修改数据的范围时,InnoDB是行锁,只锁一行的数据,写操作很快。但也有特例,比如UPDATE student SET age=10 WHERE name LIKE '王%'
,这种情况不能确定要UPDATE
的行位置,InnoDB同样会锁住整个表。
MyISAM支持全文索引,InnoDB不支持。
MyISAM保存了表的行数,InnoDB没有。也就是说,执行SELECT COUNT(*) FROM student
的操作时,MyISAM可以直接给出结果,而InnoDB要先扫描全表。不过对于加了where条件的查询操作,效果是一样的。
InnoDB下只能对自增字段单独建索引,MyISAM下可以和其它列一起建联合索引。
上面都是从网上东拼西凑总结出来的,有些博客相互还有冲突,醉了。有一点我特别奇怪,为什么MyISAM是表锁,InnoDB是行锁,MyISAM比InnoDB读性能还会好呢?搜了好多地方都没找到答案,老大苏武说其实不一定,只是某些情况会快一些,而且用MyISAM的人少。刚好最近我看公司的wiki,公司内部是强制用InnoDB做存储引擎的,我猜可能是出于事务和数据恢复的考虑。
还有其它一些区别,不一一列出来了。感觉这篇博客还有要完善的地方,不过基本的知识点已经涉及到了,有空再进一步研究吧。
实习一个多月了,很多零散的东西都在印象笔记里,加上面了几家公司,有很多想总结整理的东西。这周立了个flag,一天一篇博客,希望能不食言。
参考资料
[1] MyISAM InnoDB 区别
[2] MySQL存储引擎MyISAM与InnoDB的主要区别对比
[3] MySQL: InnoDB 还是 MyISAM?
[4] 《高性能MySQL》
那么作为一个程序员,我们只要拿起命令行武器,在系统中添加一个文件就ok了!
首先需要找到你的移动硬盘的设备号(UUID),打开终端,输入
1
|
|
这里你的硬盘名可以通过tab自动补齐,不用手动输入。可以找到一串长长的用-
分隔开的字符串,如A9667400-4E6C-4A7C-8444-54E69C3EAD5E。输入
1
|
|
就可以创建一个配置文件了。然而这时你的移动硬盘被隐藏起来了,不过还在/Volumes
文件夹中,在桌面创建一个快捷方式吧!
1
|
|
推出你的移动硬盘,再插入电脑,会发现权限已经变了,大功告成!
]]>欧拉回路即小学时就学过的一笔画问题,不再分析了,判断条件为“奇点个数为0或2个的连通图”。所以首先要判断是否是连通图,从一个结点开始遍历图,如果最终存在结点没被访问,则不是连通图;然后计算奇点个数即可。
第一次做图论的题目,怎么存就卡住了。首先想到的是邻接矩阵,但是邻接矩阵在结点数很多的情况下会占用很多内存,而且对于边数较少的图(稀疏图),大部分的边都是0,很浪费资源。所以一般用的是邻接表,开一个vector的数组,每个vector存该结点相邻的结点即可。
BFS和DFS听了很多了,即宽度优先搜索和深度优先搜索。深度优先就是访问一个结点A,假设与A相邻的结点还有B和C,接着访问B,再访问和B相邻的结点,直到不能再访问为止。DFS用到了栈,可以用递归隐式地调用栈,写起来就只有几行。BFS将与当前结点相邻的结点依次存入队列,再访问队列中的结点。
一次AC,爽爽爽!贴上代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
|
对数组的子序列(任意相连的元素)求和,找出所有和里对M的模中的最大值。
开始就想到能不能按照“最大子序列和问题”的思路解决,后来发现无论是分治递归求解还是复杂度为O(N)的算法在这个问题中都没有用,于是看了题解。
在Quora上找到了题解,看了半天才看明白。大意是,对于输入的数组v中的元素v[i],先找到v[i]之前所有数的和对M取模的值(包括v[i]),存到数组prefix中。这里有个技巧,
1 2 3 4 5 6 |
|
即v[i] = (prefix[i-1] + m) % m
。
对于新的数组prefix,保存了当前元素之前所有元素和除m的余数,对于prefix[i],如果存在j < i,使prefix[j] < prefix[i],则从j到i的这一段序列肯定不是我们想要的,这段子序列之和对m取模的值为prefix[i]-prefix[j]。所以我们要找出从prefix[0]到prefix[i-1]中比prefix[i]大的。我们需要维护一个有序数组,并且要不断往其中添加元素,C++的set由红黑树实现(题解中介绍),采用之。由于是有序的,只要找到递增数组中比prefix[i]大的第一个元素即可,即这个元素比prefix[i]大得越少越好,使用s.upper_bound(prefix[i])
找出这里的迭代器。
代码贴出来:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
|
set即集合,维护一个递增数组,两种方法注意下
1 2 3 4 5 6 |
|
upper_bound(val)
[http://www.cplusplus.com/reference/set/set/upper_bound/]函数返回比val大的第一个元素的迭代器,要注意与s.end()
比较下,看是否不存在比它大的元素。类似的函数还有lower_bound(val)
第一次交全没过!下了测试例子发现从第二组例子开始就没过了,仔细检查发现是vector没有清空搞的鬼,一堆超时的。从另一个角度也说明vector确实牛逼,被我塞了那么多组测试也没爆。。。
总之多做题、多总结吧,想不出来看题解也是好的。
]]>多年的经验告诉我,拖延基本会让人陷入一个循环:拖延->愧疚->决定改变->失败->继续拖延。人人都想治好拖延症,可我发现拖延症似乎是绝症,并不能治好,了不起也就是缓解一下,该拖还得拖,但是我们也不能放弃治疗啊!
知乎专栏幸福课里有一个关于拖延症的专题,我读完了大部分,感觉不错。动机在杭州老师分析出拖延的以下几个原因:
完整的解释可以看这篇文章
我的拖延症总离不开社交网络,拖延的时候我常常会在微博、豆瓣、知乎中间来回刷,要知道这三个网站都是无底洞,随便哪一个一层层挖下去都能看很久。
其实也并没有解决,只是稍微缓解了一点。好长一段时间里我手机的todo list软件都是爆满,150+的待办事项都很常见了、豆瓣上500+的书想读、300+的电影想看、浏览器书签里一大摞文章没看、一堆算法题没做。想到有好多事就很烦。最近发现我那些要做的事情总是存的很多,实际上分配的时间其实不多,真正沉下心来打码的时间也不多;另外,那些想做的事情都没有一个截止日期,导致我总是对要做的事感到很大压力却没有在具体的某一件事上有压力。对此,我做了几件事:
每天有自己的事之后,刷微博、微信等等的时间也少多了,形成了正反馈。
另外,专栏里还介绍了一种方法——试着和自己谈判。每当意识到自己要拖延时,告诉自己先做一会事情,比如做半个小时再玩,如果半个小时后自己进入了状态,想做事情了,那自然最好,如果不想做事了,就放松一下吧,也没关系~亲测有效。
项目管理是不是只是PM的事和程序员完全没有关系呢?我觉得不是,相反,项目的延期和程序员、设计师的关系是最大的,到今天在冰岩里合作过的几个PM我还没遇到一个能完全把控项目风险的。
软件工程课上我学到两点,一是任何软件都是有bug的,就看有没有人能发现;二是大部分项目都会延期,程序员总是对自己的能力估计过高。不算丰富的开发经验告诉我,有时接手一个任务,一开始可能觉得任务非常简单,对需求的理解逐渐加深后可能会发现做坑越多,发现自己的漏洞越多,项目的时间慢慢就不在自己掌控之内了。
在实际开发过程中,我们会经历一个产品讨论->确定需求->技术选择->开发的过程。这个过程涉及到的人会很多,就会出现沟通不足导致拖延的现象。程序员多半是脸皮薄的,有些同学会觉得自己没弄清需求再去问会让人觉得很蠢,于是开始自己琢磨,造成拖延。这时团队人员的熟悉度就非常重要,非常熟悉的同学是不可能出现这种问题的;同时对不清楚的同学的包容也很重要,如果对ta的解答不耐烦或者嘲笑ta,很可能会导致下次ta出现问题时陷入自己纠结的状态。
第二个坑就是技术坑了。毕竟我们都处于学习阶段,有不会的东西很正常,并且技术的世界本来就是个无底洞,谁能保证自己样样精通。碰到技术坑师傅教我的一定要学会Google,我也一直这么干,并且鄙视那些伸手党。但是我发现Google解决问题只适合于对要解决的问题有些了解,很明确问题所在的时候,如果完全一头雾水,就应该找人聊一聊,看自己哪些东西不懂,弄清大概后再搜索效率会很高,也不会浪费别人很多时间。其实没事的时候大家在启明就可以多聊一聊,聊聊最近碰到的一些问题、看到的一些新东西,交流技术上的困惑和烦恼是非常爽的一件事,我就很喜欢跟强哥聊遇到的一些坑和一些不理解的东西,这样大家成长都很快~
第三个坑是互相拖,尤其在人多的情况下,A和B可能都不想干活,刚好都需要跟C交流,C的活还没干完,于是A和B也不干了。这种现象很常见,尤其是客户端,设计没出图、后台没接口都能成为拖延的理由。
其实拖延是压力管理、时间管理、自我评价等等的综合问题,只是一种现象,至于根源,还是能不能和自己好好相处,实在不行,就拖吧,也没什么大不了的,不过一定要让自己舒服,不要压抑自己,身心健康比什么都重要。
]]>如果你问Python程序员他们最喜欢Python的什么,他们通常会说是Python的可读性。实际上,良好的可读性是Python设计时的一个核心思想,这是根据一个广为认可的事实 比起写,代码更容易被人读 的思想设计的。
Python代码易读易懂的一个原因是它相对完善的代码风格的规范和Pythonic的成语。
另外,当一个Python老手(Pythonista)指出一段代码不够Pythonic,通常意味着这几行代码没有按照一般的代码规范来并且没有用公认的最好的(通常是最易读的)方式表达其意图。
在某些情况下是找不到一个大家都认可的方式去用Python表达一个意图的,不过很少有这种情况。
虽然在Python中,一切黑魔法都有可能发生,但我们更推荐最明确和最直接的方式。
Bad
1 2 3 |
|
Good
1 2 |
|
在上面的正面例子中,x、y明确地被函数接收,返回了一个明确的字典。通过读第一行和最后一行,使用这个函数的开发者就能准确知道怎样使用这个函数了,然而反面教材就完全不是那回事了。
虽然有些复合语句,比如列表推导式,因为它们的简洁和丰富的表达能力被允许和推荐使用,但是将两个不能联合使用的语句放到同一行是一种很糟糕的做法。
Bad
1 2 3 4 5 6 |
|
Good
1 2 3 4 5 6 7 8 9 10 |
|
有四种不同方法向函数传递参数:
1. 位置参数是必须的并且没有默认值。它们是参数的最简单形式,并且它们适用于很少的、是函数含义一部分并且顺序很自然的函数参数。比如在send(message, recipient)
或point(x, y)
中,用函数的人不难记住这两个函数需要两个参数和参数间的顺序。
在那两个例子中,调用函数时也可以使用参数名,并且如果这样做就可以交换两个参数的顺序了,比如调用send(recipient='World', message='Hello')
和point(y=2, x=1)
但是这会降低可读性,并且比起更直接地调用send('Hello', 'World')
和point(1, 2)
这是没有必要的冗余。
2. 关键字参数
不是必须的并且有默认值。它们常常被作为可选参数传递给函数。当一个函数有超过2至3个可选参数时,它的特点就变得更让人难以记住了,这时使用有默认值的关键字参数就会很有用。比如,一个更完整的send
函数可以被定义为send(message, to, cc=None, bcc=None)
。这里cc
和bcc
是可选的,并且在没有其它参数传入时值为None
。
在Python中有多种方法调用一个有关键字参数的函数,比如可以按照参数定义的顺序而不显式地命名参数来调用,比如send('Hello', 'World', 'Cthulhu', 'God')
会向上帝发送一个密件副本。也可以像send('Hello again', 'World', bcc='God', cc='Cthulhu')
这样用其它的顺序命名参数。除非有充分的理由不按照最接近函数定义的语法send('Hello', 'World', cc='Cthulhu', bcc='God')
来,否则最好避免出现这两种可能性。
作为旁注,我要说,按照YAGNI 原则来,通常去掉一个“只是为了以防万一”而加上的可选参数(和函数当中的逻辑)比在需要时添加一个新的可选参数和它的逻辑要难。
3. 可变参数列表
第三种向函数传递参数的方法。如果一个函数的作用用一组数目可扩展的位置参数能更好地表示的话,它就能用*args
结构定义。在函数体中,args
会成为一个包含所有位置参数的元组。比如send(message, *args)
可以将每个接收者作为参数被调用:send('Hello', 'God', 'Mom', 'Cthulhu')
在函数体中args
会等价于('God', 'Mom', 'Cthulhu')
。
但是这种结构有些缺陷,要小心使用。如果一个函数接受一系列具有共同特征的参数,通常将函数定义为接受一个列表或者其它序列的参数的函数会更清晰。在这里,如果send
有多个接收者,最好显式地定义它:send(message, recipients)
并用send('Hello', ['God', 'Mom', 'Cthulhu'])
来调用它。这样,函数的使用者事先就能将接收者列表制造为一个列表,并且可以向其中传递任何的序列,包括不能被解包成其它序列的迭代器。
4. 可变关键字参数列表
最后一种向函数传递参数的方法。如果函数需要一组不确定的命名参数,就可以用kwargs结构。在函数体中,kwargs会是一个字典,其中包含所有没被函数特征中的其它关键字参数获取的有变量名的参数。
同样需要注意可变参数列表中的情形,因为类似的原因:这些强大的工具应该在的确有必要时才被用到,并且如果更简单、整洁的结构就足够表明函数的意图时就不应该使用它们。
哪些参数是位置参数、哪些参数是可选关键字参数和是否使用高级的可变参数是写函数的程序员决定的,如果能明智地遵循上面的建议,就可以写出这样的Python函数:
作为黑客的强大工具,Python有非常多的钩子和工具,能让你做几乎任何的技巧性的玩法。比如,可以做这些事:
但是所有这些选项都有很多缺点,用最直接的方法来达到你的目标总是会更好。最主要的缺点是用这些结构时可读性会大大降低。许多代码分析工具,像pylint或pyflakes,都不能解析这些“魔术”代码。
我们认为一个Python开发者应该了解这些几乎无穷无尽的可能性,因为这让我们具有信心,没有什么能阻碍我们。然而,知道怎么做、尤其是什么时候不用它们非常重要。 r
就像一个功夫高手,一个Pythonista知道如何用一只手指杀人,实际上却从不这么干。
正如上面我们看到的,Python允许很多技巧,其中有些包含潜在的危险。一个很好的例子就是,任何客户端代码都可以重载一个对象的属性和方法:Python中没有private关键字。这种和像Java这样防御性很强、有很多手段来避免误用的语言非常不同的设计思想被称为“我们都是负责的用户”。
这不意味着没有属性被认为是私有的、Python中不可能进行合适的封装。相反,Python社区不依赖开发者在自己和别人的代码之间建一堵堵墙,而是更倾向于依赖一系列的公约来指示这些元素不应该被直接访问。
对私有变量和实现细节最主要的公约就是给所有“内部变量”加上下划线前缀。如果客户端代码破坏了这种规则访问了这些被标记的元素,任何因为代码修改导致的误操作或者问题都应该由客户端承担责任。
慷慨地使用这条公约是被鼓励的:任何不想被客户端代码使用的方法或属性都应该加上一个下划线前缀。这会保证责任更好地分离、现有代码更好修改;将私有属性公开化总是可以的,但是将公有属性私有化可能就要麻烦多了。
当一个函数变得越来越复杂,不难碰到在一个函数体中用多个return语句的情况。然而为了保证清晰的缩进和可持续发展的可读性水准,最好避免从很多函数体输出点返回有意义的值。
有两种主要情况会在函数体中返回值:函数的结果在函数正常执行后返回,和指示错误输入参数或者导致函数无法完成它的计算或任务的其它情况。
如果你不希望在第二种情况中抛出异常,可能就需要返回一个值,比如None或者False来指示错误的输入参数或者其它原因导致函数不能像需要的那样正确执行。在这种情况下,在错误的上下文被发现时,越早返回越好。这会让函数的结构变得扁平化:所有那条因为错误而返回的语句之后的代码都可以假设满足进一步计算函数主要结果的条件。通常是要有多个这样的返回语句的。
然而,当一个函数在正常流程中有多个主要退出点时,调试返回结果就变得困难了,所以保持一个退出点会更好。这对制造出代码轨迹也有好处,有多个退出点可能表明你的代码需要重构了。
1 2 3 4 5 6 7 8 9 10 |
|
一个编程成语简单来说就是写代码的一种方法。编程成语的想法在c2和Stack Overflow中已经得到了充分说明。
地道的Python代码通常被称作很Pythonic。
尽管通常有一种,并且更可取地仅仅只有一种明显的方法来解决问题;那种写地道的Python代码的方法,对于新人来说可能很不明显。所以好的成语需要有意识地去掌握。
下面介绍几个常用的成语:
如果你知道一个列表或元组的长度,你可以通过解包给其中的元素命名。比如enumerate()
会为列表中的每一项提供一个二元组:
1 2 |
|
你也可以用它来交换两个变量:
1
|
|
嵌套序列解包也行:
1
|
|
Python3中,PEP 3132介绍了一个扩展的解包的新方式:
1 2 3 4 |
|
如果你需要给什么东西赋值(比如在序列解包中),但是后面不会用到那个变量,用__
:
1 2 |
|
注意:
很多Python风格指南推荐对需要抛弃的变量使用单下划线"_",而不是这里推荐的双下划线"__"。问题是单下划线"_"通常是作为[gettext()](http://docs.python.org/library/gettext.html#gettext.gettext)函数的别名来使用的,并且还被在交互式提示环境中来保存上一次操作用。用双下划线来替换掉它可以和它一样清晰,也几乎差不多方便,并且还消除了意外干扰其它用到它的情况的风险。
使用列表*
操作符
1
|
|
因为列表是可变的,*
操作符(像上面的一样)会创造一个包含N个对相同列表的引用的列表,这可能不是你想要的。我们用列表推导来代替它:
1
|
|
一个创建字符串的常见成语是对空字符串调用str.join()
1 2 |
|
这会将变量word赋值为’spam’。这条成语可以用在列表和元组上。
有时候我们需要在在一个集合中进行查找。让我们来看看两种选择:列表和字典。
例如下面的代码:
1 2 3 4 5 6 7 8 |
|
尽管这两个函数看起来几乎一模一样,因为lookup_dict
利用了Python中的字典是哈希表的事实,这两个查找函数之间的性能差异是很大的。Python会不得不遍历列表中的每个项目来找到匹配的情形,这是非常耗时的。通过分析字典的哈希,在字典中查找键值可以被非常迅速地完成。要获得更多信息看这个StackOverflow页面
也作为PEP 20被人了解,Python设计的指导原则
>>> import this
The Zen of Python, by Tim Peters
Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!
更多好的Python风格的例子,看这个Stack Overflow的问题或这些来自一个Python用户组的幻灯
PEP 8是Python实际上执行的编程风格指南。
将你的Python代码装换成PEP 8的整体上是个很好的想法,这能让你的代码在和其他开发者一起做项目时更持续。有一个命令行程序,pep8可以检查你的代码是否符合要求。在终端中输入下面的命令来安装它:
$ pip install pep8
在你的一个或者一系列文件跑一下来查看是否有任何冲突:
$ pep8 optparse.py
optparse.py:69:11: E401 multiple imports on one line
optparse.py:77:1: E302 expected 2 blank lines, found 1
optparse.py:88:5: E301 expected 1 blank line, found 0
optparse.py:222:34: W602 deprecated form of raising exception
optparse.py:347:31: E211 whitespace before '('
optparse.py:357:17: E201 whitespace after '{'
optparse.py:472:29: E221 multiple spaces before operator
optparse.py:544:21: W601 .has_key() is deprecated, use 'in'
这里有些约定你应该遵守来让你的代码更易读。
你不用显式地将一个值与True或None或0进行比较,你可以将它加到if语句后就行了。查看Truth Value Testing来查看一个值为false的列表。
Bad
1 2 3 4 5 |
|
Good
1 2 3 4 5 6 7 8 9 10 11 |
|
不要用dict.has_key()方法,用key in d
语法来代替它,或者向dict.get()方法传递一个默认参数。
Bad
1 2 3 4 5 |
|
Good
1 2 3 4 5 6 7 8 |
|
列表推导提供了一种强大的、简洁的方法来操纵列表。同样地,map()和filter()函数可以用一种不同的、更简洁的语法对列表执行操作。
Bad
1 2 3 4 5 6 |
|
Good
1 2 3 4 |
|
Bad
1 2 3 4 |
|
Good
1 2 3 4 |
|
使用enumerate()来对列表中你的位置进行计数。
1 2 3 4 5 6 7 |
|
enumerate()函数比手动处理计数器的可读性更好。更重要的是,它对迭代器的优化更好。
用with open
语法来读文件,这样会自动地为你关闭文件。
Bad
1 2 3 4 |
|
Good
1 2 3 |
|
with
语句会更好因为它会保证你总是会关闭文件,甚至在with
块中有异常抛出的情况下。
当代码逻辑上的一行比可接受的限制长时,你需要将它分割到多个物理行中。如果行尾是一个反斜杠,Python解释器会将连续的行连接起来。在有些情况下这很有用,但是通常应该避免这样做,因为这很脆弱:将一个空格加到行尾的反斜杠后就会破坏代码并且可能造成意料之外的错误。
一个更好的解决方案是在你的元素周围使用括号。Python解释器会将一个行尾未闭合括号后的下一行连接起来,直到括号闭合。同样的行为对花括号和方括号也成立。
Bad
1 2 3 4 5 6 |
|
Good
1 2 3 4 5 6 7 8 |
|
然而更多情况下,不得不分割一个逻辑行意味着你试图同时做太多事,这可能会降低可读性。
这篇文章的翻译动笔是在3月22日,今天已经是4月16日了,中间拖延了将近一个月,实际上大部分都是我今天一口气翻译完的。而且由于这篇文章一直没翻译完,导致想写的其它几篇也不能开始写。首先自我检讨下,确实没把这篇文章的翻译放在心上,因为找实习一直在打码,不过如今实习找完了,就有时间充实下自己和自己的博客了。另外,本来对这篇文章期望值相当高的,结果翻译完发现大部分内容我早已掌握,对我的帮助实际上不大,这种一直在语法上绕来绕去其实还是有点无聊,后面会翻译或者写一些更有实用价值的文章。
翻译这件事还是很难的,不过翻完这篇长文,我也掌握了一些技巧,比如it’s开头的无主被动句型要调整语序、灵活地选用更地道的汉语、长句子拆分为多个短句……总得来说还是熟能生巧。
另外,有译得不好的地方请在留言指出,请你吃棒棒糖。
]]>在Python中, 读写文件不需要import任何库, 第一步是使用open
函数获取一个文件对象
文件通常被分为文本文件
或二进制文件
, 文本文件
通常是由很多行组成的序列(sequence), 而每一行又是很多字符(characters)组成的序列. 每一行由EOL(End Of Line)
终结, 最常见的行终结符是\n
, 又叫换行符. 反斜杠(backslash)表明下一个字符将被当作一个新行(译注: 这里没理解). 基本上不是文本文件的文件就是二进制文件, 二进制文件只能被了解其文件结构的应用处理.
我们使用内置的open()
函数打开一个文件. open()
返回一个文件对象, 一般会传入两个参数. 语法是:
1
|
|
mode
参数是可以省略的, 缺省条件下为'r'
mode
参数可以是:
1 2 |
|
接下来文件对象函数将被调用. 最常用的两个函数是read
和write
.
让我们先来创建一个新文本文件. 你可以随便给它取名字, 在这个例子中我们叫它newfile.txt
1 2 3 4 |
|
现在我们打开newfile.txt
, 可以看到如下内容:
1 2 3 |
|
我们可以用不同的方法来读一个文本文件.
file.read()
如果你想得到一个包含文件中所有字符的字符串, 你可以用file.read()
1 2 |
|
输出:
hello world in the new file
and another line
我们也可以通过使用file.read(n)
来指定字符串应该返回的字符的个数, n
确定了字符个数. 下面这段代码读取文本中的前5个字符并将它们作为字符串返回.
1 2 |
|
输出:
hello
readline()
函数会逐行读取文件(而非一次读取整个文件), 调用readline()
会获取文件第一行, 之后的调用会返回接下来的行. 通常它会从文件读取单行并返回一个包含直到\n
的字符串
1 2 |
|
输出:
hello world in the new file
readlines()
将整个文件作为一个用\n
分隔的列表返回
1 2 |
|
输出:
['hello world in the new file\n', 'and another line\n']
读取文件的行时, 你可以循环遍历这个文件对象. 这在内存占用上是高效的, 并且写法简单.
1 2 3 |
|
输出:
hello world in the new file
and another line
写方法需要一个参数, 待写入的字符串. 写入后如果要换行, 在末尾添加一个\n
1 2 3 4 |
|
当你对文件的操作结束后, 调用f.close()
来关闭它并且释放打开这个文件所占用的系统资源. 调用f.close()
后, 对这个文件的操作都会失败.
让我们看看一些使用不同方法的例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
|
另一种处理文件的方式是with
语句. 使用with
语句是很好的做法. 通过with
语句, 你就有了更好的语法和异常处理.
另外, 它会自动关闭文件. with
语句提供了一种保证资源得到释放的方法.
使用with
打开文件很简单:
1
|
|
让我们来看些例子
1 2 3 |
|
同样地, 你也可以循环遍历文件对象:
1 2 3 |
|
注意, 我们不需要写file.close()
, 它会被自动调用.
with
的例子让我们通过一些例子来看看平常我们可以怎么用到它
with
写文件1 2 |
|
1 2 |
|
最后一个例子中, 我们将看到如何从一个文本文件中分割行. split
函数将变量data
中包含的字符串以空格符为分割符分割开. 你也可以根据任何你想要的分割符分割, 例如line.split(':')
会用冒号分割字符串
1 2 3 4 5 6 |
|
输出:
Because multiple values are returned by split, they are returned as an array.
['hello', 'world,', 'how', 'are', 'you', 'today?']
['today', 'is', 'saturday']
[1] http://docs.python.org/2/tutorial/inputoutput.html#reading-and-writing-files
[2] http://www.pythonforbeginners.com/cheatsheet/python-file-handling/
[3] http://en.wikibooks.org/wiki/Non-Programmer’s_Tutorial_for_Python_3/
[4] http://chryswoods.com/beginning_python/
呼, 终于译完了这篇水文…完全新手向的一篇文章, 译完才发现比我以为的要浅好多, 算是复习文件操作吧~
]]>今天一天我完成了原来用discuz搭的bbs的迁移,冰岩博客的迁移和华中大知多少的迁移,原先代码都在43上,数据库在17上,都是windows服务器,43上还有ftp服务,17上就什么都没有,dump下来的表都是用百度云上传的==!最后把它们全都放到13上了,整个都是体力劳动啊,后面应该想想一些重复的劳动怎样用脚本来解决掉。
mysqldump -u username -p dbname > name.sql
1 2 3 |
|
还是相当方便的
scp -P2012 /home/mrzero/test.txt user@example.com:/home/user
scp -P2012 user@example.com:/home/user/test.txt /home/mrzero
我用Python写了一段脚本来下上传的文件,先是在iPython里一点点测试,成功后用scp传到13上的。用到的是ftplib
,这是Python的标准库,果然是battery include啊~~
1 2 3 4 5 6 7 8 9 10 |
|
感觉写得相当挫,不过能用=。= 后面再改改能加上命令行参数就能在团队里共享,以后部署到服务器上就很欢乐了,现在简直是… 不过在部署和管理方面,shell scripts应该能更自在地工作,后面一空下来就要学学shell scripts啊!
ln -s example.com ../sites-avalible/example.com
奥,就是这个样子,明天再在自己电脑上折腾下把nginx配好吧,还要从零入门php和CI框架。哎,十一也过的这么累,不过还是进步很快的就是啦,最后谢谢胡扬大神的悉心指导,bug还要加油才是!
2014-10-4 于博客园
迁移批注:
scp
不能tab出文件名是ssh key
的问题. 之前部署网站这种重复性的劳动也用脚本解决了一些, 不过说好的运维平台还是没有搭起来, 不知什么时候可以搭起来…
即使现在想想那段每天迁网站调bug的日子还是很痛苦, 三四点睡都是家常便饭了, 最后效果还是不够好, 负载均衡没有搭起来, 单点故障的问题还是没有解决, 数据库依然是本机, 除了我用自己写的脚本部署外大家还是手动复制粘贴nginx
配置, 只能说现在还是太弱了, 运维知识不足, 对架构理解也不深, 当时都没能理解胡扬到底要我干啥. 也许到公司实习之后可以改变团队服务器的现状吧, 可那时我还会能抵住更有趣的项目和钱的诱惑回来整团队服务器吗, 我要打上一个大大的问号…
管道命令(pipe)
。第一次知道管道这个词还是在学django
的时候,模板里的过滤器很像这里的管道。管道就是将输出在标准输出中的信息一次次处理最终打印在标准输出中,所以管道命令必须是接受标准输出的命令,cp
mv
ls
都不是管道命令。
less
和tail
如ls -al /etc | less
就能用类似vi
的方式浏览在屏幕上打印的内容了。还能用vi
里的查找命令/
、?
,可以避免滚屏,非常好用。
tail filename
输出文件最后10行tail -n 5 filename
输出文件最后5行tail -F filename
监视文件的改变,一有变化就显示出来在分析error log的时候非常有用,可以查看最近的错误信息。
cut
和grep
cut针对的是输出的每一行,解释两个命令:
echo $PATH | cut -d ':' -f 5
-d 选项后面紧跟分隔符,-f表示取第5段export | cut -c 12-
-c 选项表示选取的字符数,从12到结尾,也可是类似12-15这样grep真是神一般的命令,可以配合ps aux
找到想要的进程。
参数
last | grep -v 'root'
找出非root的登陆睡觉,明天继续整理剩下的内容ww
2014-9-22 于博客园
迁移批注:
Yesterday you said tomorrow…有了”一”就再没”二”了…
不过后来除了cut
外这几个命令都很常用, 使用linux
时还是带来了很大帮助~ 需要继续学习~
一个程序被加载到内存中运行,在内存中的数据就被称为进程. 在Linux下,所有的命令在执行时都会被系统定义为一个进程
,这条命令会被分配一个ID,称为PID
,执行这条命令的用户会被分配一个UID
,系统根据UID来判别执行命令的权限。
进程就是正在运行中的程序。
子进程是父进程衍生出来的进程,用fork and exec
的方式产生,PPID
值与父进程PID
值相同可以用ps -l
来查看当前用户的所有进程。
Linux下的工作管理很像windows的任务管理器。工作管理是将进程控制在前台或后台运行,后台运行的程序必须是与用户没有交互,换句话说不用等待输入的。出现提示符让你操作的环境称为前台(foreground)
,反之称为后台(background)
可以在执行的命令后加上&
把它丢到后台去执行,如
1
|
|
终端里会显示出PID,如果有出错信息,会提示,可以看到命令提示符。
CTRL+Z可以将进程暂停放到后台,进程是没有停止的!CTRL+C是停止当前的进程。被CTRL+Z放到后台暂停的进程,可以通过fg
和bg
调整为运行状态。先查看一下当前后台工作状态:
jobs [-lrs]
fg %jobnumber
可以调到前台(%
可有可无,jobnumber为任务编号,不是PID)或是用bg %jobnumber
来在后台运行。kill -signal %jobnumber
- signal:
脱机管理是指当你用远程终端登陆服务器时,如果直接以&
方式将任务放到后台掉线之后进程是不会继续执行的,可以用nohup
来解决,如:
1
|
|
ps命令可以静态查看系统进程。
ps -l
查看自己bash相关进程ps aux
查看系统所有进程,配合grep等管道命令用2014-9-21 于博客园
迁移批注:
其实到今天那一章还是没读完…拖延就是这么产生的…
之后学习到ps aux | grep uwsgi | awk {'print $2'} | xargs sudo kill -9
这个神奇的命令, 后面还要详细学习awk
的主要用法, 目前只会这一个命令, 用于重启uwsgi
等等的进程已经足够了
对于ps
和kill
两个命令, 目前团队里大家的做法依然是简单粗暴的ps aux
和kill -9
, 感觉对kill
用得还是不够好…
jinja2
写模板的时候遇到了一些问题,记录一下
之前的小项目写得都很不规范,模板都是能用就行,基本上只用到if
语句,for
语句和变量。导航栏都是复制粘贴,没有把共同的部分抽出来。写模板的时候还应该注意一下不要直接在原来的html上改,这样容易把html改乱,应该新建一个template
目录,再一个个写模板,这样更好
参照jinja2的文档抽出公共部分,如
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
|
在子模板中填充对应的block
就行,如
对于在base.html里有但是子模板里没有的block
,对应位置会采用base.html里的内容
1 2 3 |
|
很多用到导航栏的情况都会有当前所在位置高亮的设置,假设CSS中.active
设为高亮了,那么在jinja2中就能给base.html传值,如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
|
在子模板中,向base.html传active
的值就行了,我们不只可以通过py文件向jinja2传值,还能在不同模板之间传值
1 2 |
|
这样导航栏就能根据相应的内容显示高亮的li
了!更多内容参考官方文档
jinja2支持很多Python的语法,于是我尝试调用len(lst)
函数,会报错。
要获取列表的长度,应该写成lst|length
或是它的别称lst|count
参考这个问题
2014-9-10 于博客园
迁移批注:
这篇博文由于包含jinja2
的模板语法, 在解析时会与liquid
发生冲突而报错, 于是我采用载入代码文件的方式解决了这个问题!
session
和cookie
的方式来处理登录权限问题, 而在移动应用中要验证用户身份采用登录时给用户生成一个token(令牌)
的方式. 每次用户发出需要身份认证的请求时, 就需要验证一次token
是否有效, 无效的情况包括token
无法被解析等. 另一个问题是如果token
被泄露, 用户的安全将受到威胁, 所以应当对这个token
设置一个过期时间, 超过这个时间后应当重新登录, 这样可以将用户信息泄露的风险降低.
token
有个很棒的Python第三方库叫itsdangerous
, 包含许多常见安全问题的解决方案, 比如文件名等等.
token
TimedJSONWebSignatureSerializer
能将包含用户id的字典, 如{'user_id': 1}
设置一个具有过期时间的数字证书(Signature)
, 需要注意的是, 设置的secret key
一定要足够安全, 在flask
应用中, 我们采用flask
配置中的secret key
1 2 3 4 5 6 7 8 |
|
token
合法性以及是否过期装饰器(decorator)
是Python
一个很有用的语法糖, 可以有效地减少重复代码. 在每个需要验证token
的场景都用装饰器包裹一层, 就能验证无效token
和过期token
了~
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
|
例如实现一个关注用户的操作, 在视图函数中这样调用装饰器token_required
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
可是这样每个视图函数中需要一个user_id
的参数, 这样还是存在重复, 不知是不是我装饰器用得不对:(
web.py
照着官方文档在服务器上搭好了后台。这次很奇怪地出现了一个Nginx 502 Bad Gateway的错误。
执行上面的kill `pgrep -f "python /path/to/www/index.py"`
会出现错误提示,可是启动脚本的时候明明是提示spawn-fcgi: child spawned successfully: PID: 32401
的信息的,然后还可以继续执行spawn-fcgi -d /path/to/www -f /path/to/www/index.py -a 127.0.0.1 -p 9002
的脚本
,同样会提示成功。
原因是进程确实创建成功了,但是又马上终止了!!
这时用spawn-fcgi
的-n
选项就能看到错误信息了。原来是index.py里少了一行import os
,一开始就出错了,而不是访问某个具体地址时会报错。类似的情况在php下面也容易碰到,php的语法错导致php5-fpm一开启就死掉,用php5-fpm -e
可以查看错误原因。
这里还要注意是否是按照官网上的步骤配置的web.py部署条件,有没有安装web.py, spawn-fcgi和flup,如果没有安装可以用sudo pip install web.py
,sudo apt-get install spawn-fcgi
,sudo apt-get install python-flup
(ubuntu下),没安装依赖也会导致同样的错误。
解决是google到这里的解决方案。
2014-9-10 于博客园
迁移批注:
这是在部署冰岩新官网出bug后写的博文, 其实是使用web.py
线上代码和开发环境不一致导致的(服务器采用Nginx+FastCGI, 本地是直接解释器运行), web.py
在部署时还得根据环境改代码, 这种做法是非常操蛋的, 我也没找到最佳实践, 而flask
就不存在类似问题. 另外web.py
的作者已经饮弹自尽, 没有继续开发了, 加上文档缺失等原因, web.py
已经被抛弃了, 不过它足够简单, 作为Python web开发入门的学习框架还是很合适的, 它也是我的初恋, 相爱相杀! tornado
与它写法很相似, 闲下来我会学习的!
502 Bad Gateway
其实很常见, 字面意思是网关错误, 原因几乎都是前端Nginx
将请求反向代理到后端的进程, 如uwsgi
或php-fpm
等进程管理工具跑起来的进程, 没有成功运行导致的, 这种情况首先应该检查进程有没有起, 生产环境与开发环境是否不同.
去年大概这个时候, 用jekyll
和github pages
搭了一个静态博客, 当时乱折腾把ruby环境给折腾坏了, 加上对之前的主题始终不满意(代码高亮差以及审美疲劳等…), 一年都没有更博. 然后在博客园写了十几篇博客, 隔段时间看, 擦, 写得好low!
比较好的办法就是经常写, 把那些很low的文章顶到很多页之前, 就没人笑话了.
新博客将jekyll
换成了octopress
, 因为喜欢这个博客主题, 同样是用markdown写博客, 代码高亮支持得很好, 自带的好看的solarized dark
配色, 搭建流程参看这篇博文
1 2 3 4 |
|
由于我用的是zsh
, rake new_post
命令不支持zsh
, 每次还要切回bash
才能自动生成新文章, 我在考虑写个脚本完成这件事
1
|
|
并根据文档修改navigation的配置
首先注册disqus并设置自己的shortname, 在_config.yml
中加上自己的shortname
看这篇博客, 折腾完还要Ctrl+C
再执行下rake preview
, 并且注意, 如果你的分类中有中文博客里的代码会出bug, 需要自己hack一下, 或者像我压根就不用中文做分类名…
最后推荐这份教程(不过这哥们貌似写完这份教程之后都没更博了囧)
教程在此, 修改_config.yml
, 查找到excerpt_link: "Read on →"
这一段, 默认设置是文中<!--more-->
之前的内容会显示到首页上