关于pubsubhubbub和Google Reader

上一篇文章中,我描述了自己如何重制了一个Danbooru的feed。然而,经过一夜的睡眠,我「惊喜」地发现Google Reader在睡前到醒后的7个多小时里面都没有抓取过我订阅了的这个feed——也就是说,就算重制的feed包含10000条最近更新,只要GR不主动抓取,那这些更新也还是没有意义。

绝望之下,觉得归根到底还是弄清楚pubsubhubbub(以下简称PuSH)协议的原理。试着搜索有关GR和PuSH的文章,发现@keakon曾经发表过『PubSubHubbub不通知Google Reader的原因』『实现PubSubHubbub订阅』和『使用PubSubHubbub』三篇文章。拜读之后,明白了什么叫publisher和subscriber,也得知GR可能会自动忽略订阅数少的feed的PuSH,于是先写了一个sub,然后拿菜包工作室来当小白鼠(不好意思啦@tc201158),由于这个博客刚开张,只有我一个人在GR订阅了(订阅数为1),在装了PuSH插件并在hub上进行subscribe之后果然还是无法实现PuSH,可是我用另外一个GR帐号订阅其feed(订阅数为2)之后,PuSH就能正常工作了。

大喜,以为终于找到了原因,于是对重制的feed也如此这般地弄,结果却失败了…无奈之下,我又试着对danbooru的原feed重新在hub上subscribe,发现原feed实现了PuSH的同时,重制的feed也实现了PuSH!难道因为重制的feed中提供的id、url等信息仍然是原feed的信息,所以只有在收到原feed更新的ping请求后,hub才会通知GR抓取重制的feed?

不过既然原feed能够实现PuSH,那重制的feed就没什么意义了;只需要实现在GAE上利用cron定期检查原feed是否有更新,有的话就ping一下hub就可以了。于是又浪费了一整天的人参来实现了这个功能…

至此,对Danbooru feed的PuSH改造完满完成。经验教训:

  1. 尽管有约定,但在feed中添加hub link并不是必须的,至少对于Google的hub(http://pubsubhubbub.appspot.com)是如此;
  2. 利用第一点,可以对任何一个feed实现PuSH改造。先写一个sub以便在Google的hub上对feed进行订阅(必须有订阅才能实现PuSH),然后写一个cron定期检查feed的更新并且ping hub即可;
  3. 开发GAE应用时,利用handlers和wsgi来架构整个应用,无论是应用结构还是debug都方便太多了,决定从此放弃像twitter2weiboviagtalk那样面向过程开发的方法…

题外话。今天是⑨月⑨日的⑨节,可是⑨的同人图一点都不给力啊…

重制danbooru的feed

作为一只东方众,我每天都会通过Danbooru来收一堆东方同人图——Danbooru类型的图站有一个很出色的地方,就是搜索结果可以输出rss feed,直接在GR中添加feed「http://danbooru.donmai.us/post/atom?tags=touhou」,就可以在GR中订阅D站的带有touhou标签的新投稿,十分方便。

前两天开始,我发现这个feed的更新数变少了——通常一天下来会有超过100的更新,但当天只有六十多,而且某次我在扫feed时,发现feed的上一次抓取时间是2个小时以前;而另外一次扫feed时,发现feed一下子实时更新了20篇——通常一个rss feed只会包含最近20篇投稿,这意味着由于GR的抓取频率比较慢,很可能没有办法将所有的近期更新抓全。

为了解决这个问题,我首先想到的是PubHubSubBub——一个能够在feed有更新时主动通知Hub(如GR),使其主动过来抓取更新的协议。我当然不可能直接让D站方面实现这个协议,但搜索了一下相关资料,似乎并没有要求通知者和feed一定要同一服务器,所以利用PubHubSubBub的代码在本机上测试了一下,发现即使通知了http://pubhubsubbub.appspot.com/,GR也不会主动抓取对应地址的更新…几番测试下依然无解,只能放弃了这个想法,转而考虑为D站的feed制作一个包含足够多篇数的近期更新的feed。

重制feed的应用仍然放在GAE上。初步构想了一下,应用要实现2个功能:

  1. 以比GR高得多的抓取频率(如1分钟1次)抓取D站的对应feed,并且将新投稿保存在数据库中;
  2. 输出rss feed,其中包含足够覆盖GR抓取频率的数量的新投稿数,甚至可以根据GR抓取的User-Agent智能判断那些投稿已经被GR抓取。

然后开始忙活…为了解析D站的feed,先是打算用minidom,后来发现Google Data APIs中提供了atom的解析器,于是将代码下载下来,稍微看了下doc就知道怎样用了;输出rss时则参考了PubHubSubBub代码中的Atom模板。

由于不懂如何在本地环境调试GAE应用,所以每次都要先上传到远程再调试;由于GAE的log有延迟,所以程序出错后要等几分钟才能在后台看到trackback,后来干脆直接把整个程序代码放到一个try-except块中,再用print命令把异常打印到网页上。

就这么个小东西弄了一天,半吊子的悲剧啊…

果然还是要php化

heliohost又挂了,尽管对于免费空间我们不能够期待太多,不过放在上面的ac娘工具变得不稳定,而且域名又不美观,促使我考虑为它寻找绑定一个免费的二级域名,外加一个稳定点的免费空间。

在寻找的过程中,我惊讶地发现,原来heliohost确实很强大,支持python+mysqldb,这种程度的配置原来不是每个free host都支持的!我找到了vps-hosting.ca,打算搬过去的时候才发现,python cgi中居然没有MySQLdb模块。

于是我想,能运行当前的ac娘工具的环境真是少之又少,heliohost是一个很不错的新技术实验平台(估计因此而经常当掉)。无论如何,为了让它到处可用,继续我的CI化ac娘工具的计划。

推上有人介绍一个网站,可以加密字符串:http://fliptitle.com/

python库确实强大……

由于某些论坛总是像秋天的蚂蚱,蹦达不了几天了,所以我一口气把它的一个版块的帖子全部抓了下来。本来只要再搞一个它的css文件下来就可以凑合着当归档了,不过我还是不满足于此:一大堆的<div></div>之类的html代码不是我所需要的。刚好在《dive into python》中看到过SGMLParser的用法,于是便发挥自己先前完全不会、用时边学边用、用完完全忘光的优良品质,写了一个提取帖子内容的python程序。

先说一件事,《dive into python》其实写得挺不错的,很注重描述python的特性,不过我觉得作者讲解的思路就是你要用python写出一个具有高度的适应能力、在各种各样的不同的险恶环境下都能够使用得全能型程序,有点类似于开发一个基于python的framework的工作,其中的“内省”啊“动态调用方法”啊什么的实在是让我触目惊心;我也曾看到过批评它讲得太复杂、以至于吓跑很多初学者的缺点。

对于我这种业余爱好者,它只要能帮上忙就行,例如写出一个乱七八糟但无论如何都还能用的ac娘工具就很不错了。为了参考这本书的内容,我想办法将那个原本极其通用的程序写得一点儿也不通用;对于每一个下载下来的页面,其实只要找到它括住帖子内容的html tag,然后将内容parse下来就行了。

借助于这个库的强大功能,我很轻易就能把那些<span style=”display:none”>乱码</span>的代码去掉了;而普通的浏览器拷贝是无法避免掉这些乱码的。再用glob读取当前文件夹的文件列表,批量处理,几分钟后就把几千个帖子全部转成干净的文本文件了。

python中列表和字典这两个集合类型的强大,是它好用的一个重要原因。以前用vbs写excel的宏,要进行某些棘手的操作的时候,上网查资料,会告诉你有一种叫XXX类型的对象可以处理这个情况,不过这些处理完之后返回的是另外一种YYY对象,要调用ZZZ方法才能将它print出来=.=。java更好,上网搜就说:你可以用一个XXXfactory……然后没有任何经营工厂经验的我就只好放弃了。python很多时候都是返回一个list或者dict,而且它们都是可以直接print出来而不用写for…in循环,很方便。

此外就是它有无比强大的库。我希望更多去了解一下http,例如cookie的原理什么的,然后能够自如地做各种抓取器。

ac娘工具:数据库从sqlite移到mysql

想来想去,与其先把sqlite数据库搞下来、再找个奇怪的转换工具转为mysql再弄上去,还不如直接在远程服务器上弄,注意备份就是了。

于是赶紧写了个python去搞,参考了一些资料很快就写好了。由于先前很少接触mysql,所以很多设置例如引擎啊、字符编码啊都没设置好,后来在出现乱码、又发现不能按照事务方式执行的时候,才知道应该选InnoDB引擎,然后所有地方都设置成utf8。

mysql比起sqlite真的是好太多了:习惯了用access的我,一直都以为mysql啊mssql啊这种不能作为文件随便挪的数据库非常不方便,可是现在才发现,有phpmyadmin这种强大的存在的话,mysql比要写python脚本才能好好地访问、还要设计现实界面不然就只是显示出一堆难懂的数组的sqlite好用多了;而且在有cpanel的情况下,备份也比sqlite方便。最重要的是,sqlite还分2和3,而python用的是3、php的CI用的是2,移植相当麻烦,哎。

另外,我又开始犹豫到底要不要做php的ac娘工具了:用python做出来的程序感觉快很多,特别是换了mysql后更快了,这也许和python脚本的执行效率有关,毕竟py要755权限才能执行,而php只要644。作为别人的网站的辅助工具,其实能用就行吧。

移植的时候对数据结构进行了重新设计,充分利用了mysql的特殊数据类型:分类字段就用了enum代替smallint,date和time也用了标准的时间和日期格式,所以原来的python程序除了改变数据库接口和表名之外,还有很多地方要修改,总体来说整个程序又优化了一点,而且还有优化的余地;此外,对自动抓取数据的cron也进行了修改,通过sys.path.append的方式来实现自建库的复用(原先是将几个库的代码全部拷到同一个文件中用:这已经超越了面向过程的编程,达到了汇编语言式的编程了。豆知识:cron,Linux中的时钟守护作业,其中一个特点是从根目录开始执行程序,所以不能够简单地import程序同目录下的其它库)。

折腾到这个程度,相当有成就感。

博客搬运完了

剩下一些小修小改工作。

在搜索blogbus2wordpress的方法的过程中,我了解到许多人都是从blogbus搬到独立博客当中的;而我也是其中之一。blogbus确实有很多的优点:其可定制性已经可以媲美wordpress,不过毕竟还是处于某个大环境之下,很多东西都让人忌惮;至少,还是不得不担心说错话被跨省。

huky.org这个域名挺早就入手了,可是godaddy的nameserver竟然被墙了,这是何等的悲哀啊,这个域名就像一出生就无家可归的弃儿般可怜,而申请的一个免费空间又有许多奇怪的问题(免费的代价就是经常被人拿来当白老鼠XD)。现在处于考研前几天的关键期,本来不应该“搞搞震”的,不过无所谓了,twitter api也需要有一个相对稳定的空间来放置。

很早之前不知道是谁推荐了Hostours,支持cPanel(正好在免费空间里面实践过),支持支付宝(对我来说其实无所谓),关键是有很便宜的小型空间(和dreamhosts比=.=),如果一次买2年还能便宜,不过考虑到白名单的可能性就没有这样做。嘛,就算要挪窝,也只是拷拷数据库那么简单的事情而已。

这个博客,估计也不会有很多人关注,所以能用就行,关键是里区要能用。说来感觉挺奇怪的,在blogbus我已经连续几个月没有收到过任何评论了,人缘真的已经糟糕到这个程度了吗555。空间并没有显式地说明支持python,所以ac娘工具不打算挪过来(何况是自己写的程序,不知道会不会严重损耗资源),仍然放在免费空间好了。最近的目标是用php的CodeIgniter架构重写ac娘工具,如果搞定了的话会放过来也不一定,但肯定不会用相同的域名。

python会继续学习,不过是作为一个提供便利的脚本语言而不是php的替代方案。

无论如何,国外空间的愿望已经达成了。我还有很多很多的愿望,也许这辈子都实现不完,可是只要不浪费光阴的话就不后悔。

Python Notepad#2

函数调用
可选参数:def info(object, spacing=10, collapse=1)
多种调用方法
info(odbchelper)
info(odbchelper, 12)
info(odbchelper, collapse=0)
info(spacing=15, object=odbchelper)

Build-in Functions
type(object):显示对象的类型,可以用于任何对象,type自己、Module(import物)、None都行。
str(object):将任何对象转换成字符串,包括None;对list而言,不会自动connect各个item。
dir(object):列出对象的属性和方法。
callable(object):返回对象能否被调用的布尔值。
A string does have callable methods, but the string itself is not callable.
getattr:测试对象有没有某个属性;这个属性可以是任何对象。
getattr(object, name[, default]) -> value
Get a named attribute from an object; getattr(x, ‘y’) is equivalent to x.y. When a default argument is given, it is returned when the attribute doesn’t exist; without it, an exception is raised in that case.

[mapping-expression for element in source-list if filter-expression]
example.1理解性例子

>>> li = [“a”, “mpilgrim”, “foo”, “b”, “c”, “b”, “d”, “d”]
>>> [elem for elem in li if len(elem) > 1]       1
[‘mpilgrim’, ‘foo’]
>>> [elem for elem in li if elem != “b”]         2
[‘a’, ‘mpilgrim’, ‘foo’, ‘c’, ‘d’, ‘d’]
>>> [elem for elem in li if li.count(elem) == 1] 3
[‘a’, ‘mpilgrim’, ‘foo’, ‘c’]

example.2
methodList = [method for method in dir(object) if callable(getattr(object, method))]

关于and、or

>>> ‘a’ and ‘b’         1
‘b’
>>> ” and ‘b’          2

>>> ‘a’ and ‘b’ and ‘c’ 3
‘c’

1     When using and, values are evaluated in a boolean context from left to right. 0, ”, [], (), {}, and None are false in a boolean context; everything else is true. Well, almost everything. By default, instances of classes are true in a boolean context, but you can define special methods in your class to make an instance evaluate to false. You’ll learn all about classes and special methods in Chapter 5. If all values are true in a boolean context, and returns the last value. In this case, and evaluates ‘a’, which is true, then ‘b’, which is true, and returns ‘b’.
2     If any value is false in a boolean context, and returns the first false value. In this case, ” is the first false value.
3     All values are true, so and returns the last value, ‘c’.

>>> ‘a’ or ‘b’          1
‘a’
>>> ” or ‘b’           2
‘b’
>>> ” or [] or {}      3
{}
>>> def sidefx():
…     print “in sidefx()”
…     return 1
>>> ‘a’ or sidefx()     4
‘a’

1      When using or, values are evaluated in a boolean context from left to right, just like and. If any value is true, or returns that value immediately. In this case, ‘a’ is the first true value.
2     or evaluates ”, which is false, then ‘b’, which is true, and returns ‘b’.
3     If all values are false, or returns the last value. or evaluates ”, which is false, then [], which is false, then {}, which is false, and returns {}.
4     Note that or evaluates values only until it finds one that is true in a boolean context, and then it ignores the rest. This distinction is important if some values can have side effects. Here, the function sidefx is never called, because or evaluates ‘a’, which is true, and returns ‘a’ immediately.

and-or 玩笑
成功者:

>>> a = “first”
>>> b = “second”
>>> 1 and a or b
‘first’
>>> 0 and a or b
‘second’
失败者:a是False
>>> a = “”
>>> b = “second”
>>> 1 and a or b
‘second’
安全者:
>>> a = “”
>>> b = “second”
>>> (1 and [a] or [b])[0]

[]起到某种邪恶的作用了。

一根葱函数定义:lamda

>>> g = lambda x: x*2  1
>>> g(3)
6
>>> (lambda x: x*2)(3) 2
6

只是一个使用习惯,以免无聊的一句话函数被乱扔。

函数可以作为一个对象被存到一个参数里面。也就是说,通过合适的方法,可以让一个参数变成不同的想要的函数。

info@apihelper.py函数中的processFunc(str(getattr(object, method).__doc__)),要注意为何强制转换为str(因为__doc__可能为None,那就不能作为参数传递到processFunc,因为processFunc(s)又一个s.split()调用)

‘str’.ljust(int):补空格使得字符串达到int长度。
ljust pads the string with spaces to the given length. This is what the info function uses to make two columns of output and line up all the doc strings in the second column.

类名通常这样命名:ThisIsAClass

UserDict类:就像一个字典,可以继承它然后自制行为。

实例数据最好在__init__方法中全部声明好,不然在debug的时候会感到困扰。

__getitem__、__setitem__方法,使某个类用起来比较特殊。还是用例子说明:
FileInfo是继承自一个UserDict的类。

>>> f = fileinfo.FileInfo(“/music/_singles/kairo.mp3”)
>>> f
{‘name’:’/music/_singles/kairo.mp3′}
>>> f.__getitem__(“name”) 1
‘/music/_singles/kairo.mp3’
>>> f[“name”]             2
‘/music/_singles/kairo.mp3’
>>> f
{‘name’:’/music/_singles/kairo.mp3′}
>>> f.__setitem__(“genre”, 31) 1
>>> f
{‘name’:’/music/_singles/kairo.mp3′, ‘genre’:31}
>>> f[“genre”] = 32            2
>>> f
{‘name’:’/music/_singles/kairo.mp3′, ‘genre’:32}

__repr__:对象的表达。举例说明。

>>> class magic:
…     def __repr__(self):
…         return ”’you won’t know me.”’

>>> m=magic()
>>> m
you won’t know me.
>>> print m
you won’t know me.

__cmp__:用来决定a == b形式的比较行为。
__len__:len(object)行为。
__delitem__:del object行为;可以是del object[‘key’]=>object.__delitem__(self, key),这个方括号到底怎样界定的?

私有方法/属性:在方法/属性前面加上两个下划线,如__parse
私有方法也可以强制访问,但无论如何都别这样做。

因为Python有强大的List/Dict结构,所以可以避免很多无聊的loop。

os.path模块:用来处理文件路径,文件名,扩展名这些。
os.listdir(路径):相当于dos的dir;还有os.path.isfile和os.path.isdir可以用。
os.path.normcase:不同的系统对大小写的敏感度不同,这个方法可以根据系统的不同来决定如何处理文件名的大小写问题。

glob.glob方法:强大的文件搜索工具。

>>> os.listdir(“c:\\music\\_singles\\”)
[‘a_time_long_forgotten_con.mp3’, ‘hellraiser.mp3’,
‘kairo.mp3’, ‘long_way_home1.mp3’, ‘sidewinder.mp3’,
‘spinning.mp3’]
>>> import glob
>>> glob.glob(‘c:\\music\\_singles\\*.mp3’)
[‘c:\\music\\_singles\\a_time_long_forgotten_con.mp3’,
‘c:\\music\\_singles\\hellraiser.mp3’,
‘c:\\music\\_singles\\kairo.mp3’,
‘c:\\music\\_singles\\long_way_home1.mp3’,
‘c:\\music\\_singles\\sidewinder.mp3’,
‘c:\\music\\_singles\\spinning.mp3’]
>>> glob.glob(‘c:\\music\\_singles\\s*.mp3’)
[‘c:\\music\\_singles\\sidewinder.mp3’,
‘c:\\music\\_singles\\spinning.mp3’]
>>> glob.glob(‘c:\\music\\*\\*.mp3’)

RE指南
^    字符串开头
$    字符串尾。
\b    单词边界。(在大蛇中,\需要被转义,故表达成\\b)
\d    数字
\D    非数字
{n}、{n, m}重复n次,或重复n-m次
()    将小括号内的东西标记成一组,以便等下可以访问。
要反对python转义,用raw表达式:r’someregularexp’
+    +之前的内容重复1次或以上
*    *之前的内容重复0次或以上
c?    如果有,就必须是c;也就是说,没有任何东西也可以,但有就必须是c

稀疏(?)正则表达式:可以添加注释。

>>> pattern = “””
    ^                   # beginning of string
    M{0,4}              # thousands – 0 to 4 M’s
    (CM|CD|D?C{0,3})    # hundreds – 900 (CM), 400 (CD), 0-300 (0 to 3 C’s),
                        #            or 500-800 (D, followed by 0 to 3 C’s)
    (XC|XL|L?X{0,3})    # tens – 90 (XC), 40 (XL), 0-30 (0 to 3 X’s),
                        #        or 50-80 (L, followed by 0 to 3 X’s)
    (IX|IV|V?I{0,3})    # ones – 9 (IX), 4 (IV), 0-3 (0 to 3 I’s),
                        #        or 5-8 (V, followed by 0 to 3 I’s)
    $                   # end of string
    “””
>>> re.search(pattern, ‘M’, re.VERBOSE) #在使用的时候加上一个re.VERBOSE。
<_sre.SRE_Match object at 0x008EEB48>
>>> re.search(pattern, ‘MCMLXXXIX’, re.VERBOSE)
<_sre.SRE_Match object at 0x008EEB48>
>>> re.search(pattern, ‘MMMMDCCCLXXXVIII’, re.VERBOSE)
<_sre.SRE_Match object at 0x008EEB48>
>>> re.search(pattern, ‘M)

a = re.compile(pattern)
a.search(str).groups():返回一个tuple,包含找到的pattern中找到的组。

Python Helps Python帮上忙了

In my feed updated, there is someone who has grasped all the VeryCD data and made a knock-off abroad. He spent a week on it, applying Python for crawler and web, cron job (maybe to grasp regularly) and so forth, oops!

在我的供稿更新中,有人抓了vc的全部数据然后做了一个山寨版放在国外。他花了一个星期用Python做了爬虫和网站,也用到了时钟守护任务(也许是定期抓数据)等。~\(≧▽≦)/~

I’m doing the same for Acfun! But what he did must be much greater. What’s more, I only indexed Acfun, but he duplicated VeryCD. What makes sense lies in that we both used Python. The urllib2 is a good guy.

我也正在为ac做同样的事!不过他做的事伟大得多。此外,我只是做了ac的索引,而他复制了vc。比较有意义的在于我们都用了Python。urllib2是好东西。

Python’s excellent designed, I found as I slightly dived in to it. It’s not easy for me whose foundation of coding is bad to develop well-structured applet, but I still want to continue because I expect that it will benefit.

Python的设计非常赞,这是我稍微深入了解了一下之后发现的。但对于没有良好的写代码基础的我来说,写一个结构良好的小程序很不容易,不过我将继续努力,因为我希望它能带来好处。

Python Notepad#1

Datatypes 大蛇的数据类型
1、dyanmically typed: Types are discovered at execution time; the opposite of statically typed. VBScript and Python are dynamically typed, because they figure out what type a variable is when you first assign it a value.
动态类型:数据类型在执行的时候才被确定,和静态类型相反。大蛇和vbs类似,它们都在第一次赋值的时候才确定变量的类型。
2、strongly typed: Types are always enforced. Java and Python are strongly typed. If you have an integer, you can’t treat it like a string without explicitly converting it.
强类型:数据类型总是被强调,大蛇和java都是强类型。例如,整型不能被当成字符串对待,除非明确转换成字符串。@Diky: str(a_int)
Once a variable has a datatype, it actually matters.
当变量有了数据类型,就需要在乎这件事(?)
@Diky:
1、在使用涉及SQL的类和方法的时候,考虑将全部输入都强制转换成字符串(包括整型,None除外),这样用的时候就不用考虑太多。
2、或者是什么就用什么,OO就要反映真实的世界(被踢飞)
3、还有麻烦的编码问题,如果不能够自动检测编码,就每种编码都try-except一下直到牺牲好了。

Documenting 函数的文档(doc)
不同于注释,特指一个函数的说明性文字。
@Diky:
1、类可以用吗?
2、以前一直用#,要用”””内容(多行)”””。
3、如果有文档,必须在冒号之后马上跟随。它可以作为函数的变量被调用。
You don’t technically need to give your function a doc string, but you always should.

print yourFunction.__doc__
@Diky:返回doc。

@Diky:关于OO,我想应该将任何东西都放到类里面……例如首页只有一句话:myProgram(myConfiguration(Environment_Int), other, …)

When you want to use functions defined in imported modules, you need to include the module name.
要用模块的函数要跟上模块名。@Diky:为了代码可以被到处复制,考虑什么时候用 from XXX import XXX。

导入模块的路径问题

>>> import sys—————–1
>>> sys.path——————-2
[”, ‘/usr/local/lib/python2.2’, ‘/usr/local/lib/python2.2/plat-linux2’,
‘/usr/local/lib/python2.2/lib-dynload’, ‘/usr/local/lib/python2.2/site-packages’,
‘/usr/local/lib/python2.2/site-packages/PIL’, ‘/usr/local/lib/python2.2/site-packages/piddle’]
>>> sys————————3
<module ‘sys’ (built-in)>
>>> sys.path.append(‘/my/new/path’)-4

@Diky:
1、对本地/远程调试、Linux Cron Job很重要,因为Cron Job调用自制模块时需要绝对路径,到现在还搞不太清楚。
2、可以手动增加系统路径,不过我更关心如何让一个相对路径的模块无论如何都能被调用。

大蛇对象
In Python, the definition is looser; some objects have neither attributes nor methods , and not all objects are subclassable. But everything is an object in the sense that it can be assigned to a variable or passed as an argument to a function.
有些对象冇变量冇方法,有些对象不能被继承。不过从意义上讲,对象是因为1、可以被赋值2、可以作为参数传递给函数。
Everything in Python is an object。大蛇的所有东西都是对象。
@Diky:被封装的世界啊。

代码识别缩进:不一定是4个空格,但一定要是同样的缩进。
@Diky:所以如果用notepad2去写代码就要将制表符弄成4空格模式。

print ‘some text’,
@Diky:
1、这个逗号的意思不是不换行,而是print可以连续输出若干个对象:print ‘text1’, ‘text2’,从这个层面去理解。
2、我觉得print应该不输出回车,就不会出现这种奇怪的语法和理解方法了。

Python does not support in-line assignment, so there’s no chance of accidentally assigning the value you thought you were comparing.
大蛇不支持嵌入式比较兼赋值(i==i++这种?),所以没有什么解析上的麻烦。

对象的固有属性__name__
Modules are objects, and all modules have a built-in attribute __name__. A module’s __name__ depends on how you’re using the module. If you import the module, then __name__ is the module’s filename, without a directory path or file extension. But you can also run the module directly as a standalone program, in which case __name__ will be a special default value, __main__.
如果是import的,那么模块的__name__的值就是模块名;如果直接运行它,那值就变成__main__。
@Diky:一个模块被直接运行的话执行代码A,被引用就不执行,就应该将A放到这个判断之中。
Knowing this, you can design a test suite for your module within the module itself by putting it in this if statement. …This makes it easier to develop and debug new modules before integrating them into a larger program.
@Diky:用来做测试挺好。

Native Datatypes
Dictionaries: 字典,好理解。
@Diky:我想一个Ac投稿的数据结构应该从7元素的list修改为7个元素的Dictionaries。面向现实吧,骚年。
字典是没有顺序的,不要考虑通过某种序列去访问它。
@Diky:为了更好地引用一个投稿对象,通过合适的方法使得acPost[‘ac_id’]变成acPost.ac_id。
1 Dictionaries aren’t just for strings. Dictionary values can be any datatype, including strings, integers, objects, or even other dictionaries. And within a single dictionary, the values don’t all need to be the same type; you can mix and match as needed.
2 Dictionary keys are more restricted, but they can be strings, integers, and a few other types. You can also mix and match key datatypes within a dictionary.
大蛇的字典的value类型可以是混合的,但key类型有限制。@Diky:至少将key从[‘ac_id’]变成[ac_id],有没有这种定义方法?

del dict[‘key’]可以用来删除一个字典项。
dict.clear()方法清空掉字典,字典将变成{}。

List的负数序列号可以用来倒过来访问List,List[-1]表示最后一个元素。@Diky:非常方便。
子List
starting with the first slice index, up to but not including the second slice index.
List[1:3]:入手#1、#2元素
List[1:-1]:入手#1到最后一个元素(但不包含最后一个)
Reading the list from left to right, the first slice index specifies the first element you want, and the second slice index specifies the first element you don’t want.
1、先决定两个元素的位置;2、从左往右看;3、第一个元素表示其右边都是想要的,第二个元素表示其右边都是不想要的。
zero index可以去掉,也就是list[0:3]和list[:3]相同。
@Diky:
1、用实践来证明li[:]和li的不同:一个是引用,一个是赋值。

>>> li = [‘a’, ‘b’, ‘c’]
>>> li
[‘a’, ‘b’, ‘c’]
>>> li[:]
[‘a’, ‘b’, ‘c’]
>>> a=li
>>> a.append(‘d’)
>>> li
[‘a’, ‘b’, ‘c’, ‘d’]
>>> b=li[:]
>>> b.append(‘e’)
>>> li
[‘a’, ‘b’, ‘c’, ‘d’]
>>> b
[‘a’, ‘b’, ‘c’, ‘d’, ‘e’]
2、程序2:参数传递指南
>>> def m(a):
…  a.append(‘a’)

>>> a=[‘a’]
>>> m(a)
>>> a
[‘a’, ‘a’]

List方法
append(value):加元素
insert(index, value):插入元素
extend(list):拼List
index(value):根据value输出index
value in list:测试list中是否有value
@Diky:比起自行try..except,大蛇已经提供了超赞的测试方法了。

额外:What is false
    * 0 is false; all other numbers are true.
    * An empty string (“”) is false, all other strings are true.
    * An empty list ([]) is false; all other lists are true.
    * An empty tuple (()) is false; all other tuples are true.
    * An empty dictionary ({}) is false; all other dictionaries are true.

remove(value):删value,删左起第一个。
pop():和栈的pop一样,pop最后一个,并且输出它(可以print出来)。
List操作符
list = list + otherlist has the same result as list.extend(otherlist)
li += [‘two’] is equivalent to li.extend([‘two’])
li = [1, 2] * 3 is equivalent to li = [1, 2] + [1, 2] + [1, 2]

Tuples
不变的List。和List的区别是,用括号的。
可以入手子Tuple,规矩像list一样
不可以remove/insert/index,不过可以value in tuple的测试。
Tuple的好处:
1、更快,例如用来定义常量~\(≧▽≦)/~;
2、代码写保护;
3、可以用作字典key,但list不可以,因为字典key要求不变的。当然,用作tuple的key里面也不能有list这种可变的物体。
list(tuple)和tuple(list)可以用来在list和tuple间转化。

定义变量
    myParams = {“server”:”mpilgrim”, \
                “database”:”master”, \
                “uid”:”sa”, \
                “pwd”:”secret” \
                }
1、注意缩进;
2、backslash (“\”) serving as a line-continuation marker.
注:用了行连接的话下一行其实可以不缩进,但还是缩进吧。
不定义的话是不准引用的。有一天会感谢这个设定?
多重赋值

>>> v = (‘a’, ‘b’, ‘e’)
>>> (x, y, z) = v
>>> x
‘a’
>>> y
‘b’
>>> z
‘e’
连续赋值
>>> range(7)
[0, 1, 2, 3, 4, 5, 6]
>>> (MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY) = range(7) 2
>>> MONDAY
0
>>> TUESDAY
1
>>> SUNDAY
6

格式化字符串
‘%s=%d’ % (str, id)
后面的部分是tuple,要注意到。

格式化字符串和操作符的比较

>>> uid = “sa”
>>> pwd = “secret”
>>> print pwd + ” is not a good password for ” + uid      1
secret is not a good password for sa
>>> print “%s is not a good password for %s” % (pwd, uid) 2
secret is not a good password for sa
>>> userCount = 6
>>> print “Users connected: %d” % (userCount, )           3 4
Users connected: 6
>>> print “Users connected: ” + userCount                 5
Traceback (innermost last):
  File “<interactive input>”, line 1, in ?
TypeError: cannot concatenate ‘str’ and ‘int’ objects
1      + is the string concatenation operator.
2     In this trivial case, string formatting accomplishes the same result as concatentation.
3     (userCount, ) is a tuple with one element. Yes, the syntax is a little strange, but there’s a good reason for it: it’s unambiguously a tuple. In fact, you can always include a comma after the last element when defining a list, tuple, or dictionary, but the comma is required when defining a tuple with one element. If the comma weren’t required, Python wouldn’t know whether (userCount) was a tuple with one element or just the value of userCount.当tuple只有一个元素的时候,要加一个逗号;平时的话加不加都可以。
4     String formatting works with integers by specifying %d instead of %s.比起自行转换为Str,用这个会更好!重要!不要写垃圾代码了!
5     Trying to concatenate a string with a non-string raises an exception. Unlike string formatting, string concatenation works only when everything is already a string.
格式化字符串和c一样有很多丰富的用法,略过了。

摆弄字符串、字典和列表(有例子就好理解)

>>> params = {“server”:”mpilgrim”, “database”:”master”, “uid”:”sa”, “pwd”:”secret”}
>>> params.keys()   1
[‘server’, ‘uid’, ‘database’, ‘pwd’]
>>> params.values() 2
[‘mpilgrim’, ‘sa’, ‘master’, ‘secret’]
>>> params.items()  3
[(‘server’, ‘mpilgrim’), (‘uid’, ‘sa’), (‘database’, ‘master’), (‘pwd’, ‘secret’)]
>>> params.items()
[(‘server’, ‘mpilgrim’), (‘uid’, ‘sa’), (‘database’, ‘master’), (‘pwd’, ‘secret’)]
>>> [k for k, v in params.items()]
[‘server’, ‘uid’, ‘database’, ‘pwd’]
>>> [v for k, v in params.items()]
[‘mpilgrim’, ‘sa’, ‘master’, ‘secret’]
>>> [“%s=%s” % (k, v) for k, v in params.items()]
[‘server=mpilgrim’, ‘uid=sa’, ‘database=master’, ‘pwd=secret’]
>>> [“%s=%s” % (k, v) for k, v in params.items()]
[‘server=mpilgrim’, ‘uid=sa’, ‘database=master’, ‘pwd=secret’]
>>> “;”.join([“%s=%s” % (k, v) for k, v in params.items()])
‘server=mpilgrim;uid=sa;database=master;pwd=secret’

关于join(list_of_str):列表一定要是字符串的列表,它不做类型转换。

拆str

>>> li = [‘server=mpilgrim’, ‘uid=sa’, ‘database=master’, ‘pwd=secret’]
>>> s = “;”.join(li)
>>> s
‘server=mpilgrim;uid=sa;database=master;pwd=secret’
>>> s.split(“;”)
[‘server=mpilgrim’, ‘uid=sa’, ‘database=master’, ‘pwd=secret’]
>>> s.split(“;”, 1) #这个可选的参数1表示只拆1次
[‘server=mpilgrim’, ‘uid=sa;database=master;pwd=secret’]