附录
最后更新于:2022-04-01 06:07:34
## 附录A:JavaScript中的保留字
**下列JavaScript保留字应该避免在属性名中使用**
下面的但在在JavaScript语言中被保留,不能作为在点访问符中使用。这份名单代表此时的最佳关键字的知识;列表可能会改变或根据您的特定的执行环境更改。
下面是JavaScript语言中的保留字,且不能在点访问符中使用。这份清单集合了当前最新的关键字,该清单可能会根据具体的执行环境而有所变更或改变。
来自[ECMAScript 语言规范第五版](http://www.ecma-international.org/publications/standards/Ecma-262.htm)
~~~
abstract
boolean break byte
case catch char class const continue
debugger default delete do double
else enum export extends
false final finally float for function
goto
if implements import in instanceof int interface
let long
native new null
package private protected public
return
short static super switch synchronized
this throw throws transient true try typeof
var volatile void
while with
yield
~~~
除了特别[说明](http://code.google.com/policies.html),该页面的内容均由[共同创作协议](http://creativecommons.org/licenses/by/3.0/)(CC BY 3.0)授权许可,示例代码均由[Apache 2.0](http://www.apache.org/licenses/LICENSE-2.0)许可证授权许可)
示例
最后更新于:2022-04-01 06:07:31
## YouTube JSON API
这是YouTube JSON API响应对象的示例。你可以从中学到更多关于YouTube JSON API的内容:[http://code.google.com/apis/youtube/2.0/developers_guide_jsonc.html](http://code.google.com/apis/youtube/2.0/developers_guide_jsonc.html)
~~~
{
"apiVersion": "2.0",
"data": {
"updated": "2010-02-04T19:29:54.001Z",
"totalItems": 6741,
"startIndex": 1,
"itemsPerPage": 1,
"items": [
{
"id": "BGODurRfVv4",
"uploaded": "2009-11-17T20:10:06.000Z",
"updated": "2010-02-04T06:25:57.000Z",
"uploader": "docchat",
"category": "Animals",
"title": "From service dog to SURFice dog",
"description": "Surf dog Ricochets inspirational video ...",
"tags": [
"Surf dog",
"dog surfing",
"dog",
"golden retriever",
],
"thumbnail": {
"default": "http://i.ytimg.com/vi/BGODurRfVv4/default.jpg",
"hqDefault": "http://i.ytimg.com/vi/BGODurRfVv4/hqdefault.jpg"
},
"player": {
"default": "http://www.youtube.com/watch?v=BGODurRfVv4&feature=youtube_gdata",
"mobile": "http://m.youtube.com/details?v=BGODurRfVv4"
},
"content": {
"1": "rtsp://v5.cache6.c.youtube.com/CiILENy73wIaGQn-Vl-0uoNjBBMYDSANFEgGUgZ2aWRlb3MM/0/0/0/video.3gp",
"5": "http://www.youtube.com/v/BGODurRfVv4?f=videos&app=youtube_gdata",
"6": "rtsp://v7.cache7.c.youtube.com/CiILENy73wIaGQn-Vl-0uoNjBBMYESARFEgGUgZ2aWRlb3MM/0/0/0/video.3gp"
},
"duration": 315,
"rating": 4.96,
"ratingCount": 2043,
"viewCount": 1781691,
"favoriteCount": 3363,
"commentCount": 1007,
"commentsAllowed": true
}
]
}
}
~~~
## 分页示例
如何将Google搜索条目作为JSON对象展现出来,对分页变量也有特别关注。
这个示例仅用作说明。下面的API实际上并不存在。
这是Google搜索结果页面的示例: [![image](https://camo.githubusercontent.com/c8ff0de46d7cb966ddae8b3350960400fdd6962d/687474703a2f2f676f6f676c652d7374796c6567756964652e676f6f676c65636f64652e636f6d2f73766e2f7472756e6b2f6a736f6e637374796c6567756964655f6578616d706c655f30312e706e67)](https://camo.githubusercontent.com/c8ff0de46d7cb966ddae8b3350960400fdd6962d/687474703a2f2f676f6f676c652d7374796c6567756964652e676f6f676c65636f64652e636f6d2f73766e2f7472756e6b2f6a736f6e637374796c6567756964655f6578616d706c655f30312e706e67)
[![image](https://camo.githubusercontent.com/0341a3abf1faaffd6dc67039efd964336d285f54/687474703a2f2f676f6f676c652d7374796c6567756964652e676f6f676c65636f64652e636f6d2f73766e2f7472756e6b2f6a736f6e637374796c6567756964655f6578616d706c655f30322e706e67)](https://camo.githubusercontent.com/0341a3abf1faaffd6dc67039efd964336d285f54/687474703a2f2f676f6f676c652d7374796c6567756964652e676f6f676c65636f64652e636f6d2f73766e2f7472756e6b2f6a736f6e637374796c6567756964655f6578616d706c655f30322e706e67)
这是该页面JSON形式的呈现:
~~~
{
"apiVersion": "2.1",
"id": "1",
"data": {
"query": "chicago style pizza",
"time": "0.1",
"currentItemCount": 10,
"itemsPerPage": 10,
"startIndex": 11,
"totalItems": 2700000,
"nextLink": "http://www.google.com/search?hl=en&q=chicago+style+pizza&start=20&sa=N"
"previousLink": "http://www.google.com/search?hl=en&q=chicago+style+pizza&start=0&sa=N",
"pagingLinkTemplate": "http://www.google.com/search/hl=en&q=chicago+style+pizza&start={index}&sa=N",
"items": [
{
"title": "Pizz'a Chicago Home Page"
// More fields for the search results
}
// More search results
]
}
}
~~~
这是如何展现屏幕截图中的色块的例子(背景颜色对应下图中的颜色)
* Results 11 - 20 of about 2,700,000 = startIndex
* Results 11 - 20 of about 2,700,000 = startIndex + currentItemCount - 1
* Results 11 - 20 of about 2,700,000 = totalItems
* Search results = items (formatted appropriately)
* Previous/Next = previousLink / nextLink
* Numbered links in "Gooooooooooogle" = Derived from "pageLinkTemplate". The developer is responsible for calculating the values for {index} and substituting those values into the "pageLinkTemplate". The pageLinkTemplate's {index} variable is calculated as follows:
* Index #1 = 0 * itemsPerPage = 0
* Index #2 = 2 * itemsPerPage = 10
* Index #3 = 3 * itemsPerPage = 20
* Index #N = N * itemsPerPage
属性顺序
最后更新于:2022-04-01 06:07:29
在JSON对象中属性可有任意顺序。然而,在某些情况下,有序的属性可以帮助分析器快速解释数据,并带来更好的性能。在移动环境下的解析器就是个例子,在这种情况下,性能和内存是至关重要的,不必要的解析也应尽量避免。
## Kind属性
**Kind属性应为第一属性**
假设一个解析器负责将一个原始JSON流解析成一个特定的对象。_kind_属性会引导解析器将适合的对象实例化。因而它应该是JSON对象的第一个属性。这仅适用于对象有一个kind属性的情况(通常可以在_data_和_items_属性中找到)。
## Items属性
**_items_应该是_data_对象的最后一个属性**
这使得阅读每一个具体条目前前已读所有的集合属性。在有很多条目的情况下,这样就避免了开发人员只需要从数据的字段时不必要的解析这些条目。
这让阅读所有集合属性先于阅读单个条目。如遇多个条目的情况,当开发者仅需要数据中的字段时,这就可避免解析不必要的条目。
属性顺序示例:
~~~
// "kind" 属性区分 "album" 和 "photo".
// "Kind" 始终是它父对象的第一个属性.
// "items" 属性是 "data" 对象的最后一个属性.
{
"data": {
"kind": "album",
"title": "My Photo Album",
"description": "An album in the user's account",
"items": [
{
"kind": "photo",
"title": "My First Photo"
}
]
}
}
~~~
错误对象中的保留属性名
最后更新于:2022-04-01 06:07:27
JSON对象的_error_属性应包含以下属性。
### [](https://github.com/darcyliu/google-styleguide/blob/master/JSONStyleGuide.md#errorcode)error.code
~~~
属性值类型: 整数(integer)
父节点: error
~~~
表示该错误的编号。这个属性通常表示HTTP响应码。如果存在多个错误,_code_应为第一个出错的错误码。
示例:
~~~
{
"error":{
"code": 404
}
}
~~~
### [](https://github.com/darcyliu/google-styleguide/blob/master/JSONStyleGuide.md#errormessage)error.message
~~~
属性值类型: 字符串(string)
父节点: error
~~~
一个人类可读的信息,提供有关错误的详细信息。如果存在多个错误,_message_应为第一个错误的错误信息。
示例:
~~~
{
"error":{
"message": "File Not Found"
}
}
~~~
### [](https://github.com/darcyliu/google-styleguide/blob/master/JSONStyleGuide.md#errorerrors)error.errors
~~~
属性值类型: 数组(array)
父节点: error
~~~
包含关于错误的附加信息。如果服务返回多个错误。_errors_数组中的每个元素表示一个不同的错误。
示例:
~~~
{ "error": { "errors": [] } }
~~~
### [](https://github.com/darcyliu/google-styleguide/blob/master/JSONStyleGuide.md#errorerrorsdomain)error.errors[].domain
~~~
属性值类型: 字符串(string)
父节点: error.errors
~~~
服务抛出该错误的唯一识别符。它帮助区分服务的从普通协议错误(如,找不到文件)中区分出具体错误(例如,给日历插入事件的错误)。
示例:
~~~
{
"error":{
"errors": [{"domain": "Calendar"}]
}
}
~~~
### [](https://github.com/darcyliu/google-styleguide/blob/master/JSONStyleGuide.md#errorerrorsreason)error.errors[].reason
~~~
属性值类型: 字符串(string)
父节点: error.errors
~~~
该错误的唯一识别符。不同于_error.code_属性,它不是HTTP响应码。
示例:
~~~
{
"error":{
"errors": [{"reason": "ResourceNotFoundException"}]
}
}
~~~
### [](https://github.com/darcyliu/google-styleguide/blob/master/JSONStyleGuide.md#errorerrorsmessage)error.errors[].message
~~~
属性值类型: 字符串(string)
父节点: error.errors
~~~
一个人类可读的信息,提供有关错误的更多细节。如果只有一个错误,该字段应该与_error.message_匹配。
示例:
~~~
{
"error":{
"code": 404
"message": "File Not Found",
"errors": [{"message": "File Not Found"}]
}
}
~~~
### [](https://github.com/darcyliu/google-styleguide/blob/master/JSONStyleGuide.md#errorerrorslocation)error.errors[].location
~~~
属性值类型: 字符串(string)
父节点: error.errors
~~~
错误发生的位置(根据_locationType_字段解释该值)。
示例:
~~~
{
"error":{
"errors": [{"location": ""}]
}
}
~~~
### [](https://github.com/darcyliu/google-styleguide/blob/master/JSONStyleGuide.md#errorerrorslocationtype)error.errors[].locationType
~~~
属性值类型: 字符串(string)
父节点: error.errors
~~~
标明如何解释_location_属性。
示例:
~~~
{
"error":{
"errors": [{"locationType": ""}]
}
}
~~~
### [](https://github.com/darcyliu/google-styleguide/blob/master/JSONStyleGuide.md#errorerrorsextendedhelp)error.errors[].extendedHelp
~~~
属性值类型: 字符串(string)
父节点: error.errors
~~~
help text的URI,使错误更易于理解。
示例:(注:原示例这里有笔误,中文版这里做了校正)
~~~
{
"error":{
"errors": [{"extendedHelp": "http://url.to.more.details.example.com/"}]
}
}
~~~
### [](https://github.com/darcyliu/google-styleguide/blob/master/JSONStyleGuide.md#errorerrorssendreport)error.errors[].sendReport
~~~
属性值类型: 字符串(string)
父节点: error.errors
~~~
report form的URI,服务用它来收集错误状态的数据。该URL会预先载入描述请求的参数
示例:
~~~
{
"error":{
"errors": [{"sendReport": "http://report.example.com/"}]
}
}
~~~
用于链接的保留属性名
最后更新于:2022-04-01 06:07:24
下面的属性位于_data_对象中,用来表示对其他资源的引用。有两种形式的链接属性:1)对象,它可以包含任何种类的引用(比如JSON-RPC对象),2)URL字符串,表示资源的URIs(后缀总为'Link')。
### [](https://github.com/darcyliu/google-styleguide/blob/master/JSONStyleGuide.md#dataself--dataselflink)data.self / data.selfLink
~~~
属性值类型: 对象(object)/字符串(string)
父节点: data
~~~
自身链接可以用于取回条目数据。比如,在用户的Picasa相册中,条目中的每个相册对象都会包含一个_selfLink_用于检索这个相册的相关数据。
示例:
~~~
{
"data": {
"self": { },
"selfLink": "http://www.google.com/feeds/album/1234"
}
}
~~~
### [](https://github.com/darcyliu/google-styleguide/blob/master/JSONStyleGuide.md#dataedit--dataeditlink)data.edit / data.editLink
~~~
属性值类型: 对象(object)/字符串(string)
父节点: data
~~~
编辑链接表明用户可以发送更新或删除请求。这对于REST风格的APIs很有用。该链接仅在用户能够更新和删除该条目时提供。
示例:
~~~
{
"data": {
"edit": { },
"editLink": "http://www.google.com/feeds/album/1234/edit"
}
}
~~~
### [](https://github.com/darcyliu/google-styleguide/blob/master/JSONStyleGuide.md#datanext--datanextlink)data.next / data.nextLink
~~~
属性值类型: 对象(object)/字符串(string)
父节点: data
~~~
该下一页链接标明如何取得更多数据。它指明载入下一组数据的位置。它可以同_itemsPerPage_,_startIndex_ 和 _totalItems_属性一起使用用于分页数据。
示例:
~~~
{
"data": {
"next": { },
"nextLink": "http://www.google.com/feeds/album/1234/next"
}
}
~~~
### [](https://github.com/darcyliu/google-styleguide/blob/master/JSONStyleGuide.md#dataprevious--datapreviouslink)data.previous / data.previousLink
~~~
属性值类型: 对象(object)/字符串(string)
父节点: data
~~~
该上一页链接标明如何取得更多数据。它指明载入上一组数据的位置。它可以连同_itemsPerPage_,_startIndex_ 和 _totalItems_属性用于分页数据。
示例:
~~~
{
"data": {
"previous": { },
"previousLink": "http://www.google.com/feeds/album/1234/next"
}
}
~~~
用于分页的保留属性名
最后更新于:2022-04-01 06:07:22
下面的属性位于_data_对象中,用来给一列数据分页。一些语言和概念是从OpenSearch规范中借鉴过来的。
下面的分页数据允许各种风格的分页,包括:
* 上一页/下一页 - 允许用户在列表中前进和后退,一次一页。_nextLink_ 和_previousLink_属性 (下面的"链接保留属性名"部分有描述) 用于这种风格的分页。
* 基于索引的分页 - 允许用户直接跳到条目列表的某个条目位置。例如,要从第200个条目开始载入10个新的条目,开发者可以给用户提供一个URL的查询字符串_?startIndex=200_。
* 基于页面的分页 - 允许用户直接跳到条目内的具体页。这跟基于索引的分页很类似,但节省了开发者额外的步骤,不需再为新一页的条目计算条目索引。例如,开发人员可以直接跳到第20页,而不是跳到第200条条目。基于页面分页的网址,可以使用查询字符串_?page=1_或_?page=20_。_pageIndex_和 _totalPages_ 属性用作这种风格的分页.
在这份指南的最后可以找到如何使用这些属性来实现分页的例子。
### [](https://github.com/darcyliu/google-styleguide/blob/master/JSONStyleGuide.md#datacurrentitemcount)data.currentItemCount
~~~
属性值类型: 整数(integer)
父节点: data
~~~
结果集中的条目数目。应该与items.length相等,并作为一个便利属性提供。例如,假设开发者请求一组搜索条目,并且要求每页10条。查询集共有14条。第一个条目页将会有10个条目,因此_itemsPerPage_和_currentItemCount_都应该等于10。下一页的条目还剩下4条;_itemsPerPage_仍然是10,但是_currentItemCount_是4.
示例:
~~~
{
"data": {
// "itemsPerPage" 不需要与 "currentItemCount" 匹配
"itemsPerPage": 10,
"currentItemCount": 4
}
}
~~~
### [](https://github.com/darcyliu/google-styleguide/blob/master/JSONStyleGuide.md#dataitemsperpage)data.itemsPerPage
~~~
属性值类型: 整数(integer)
父节点: data
~~~
items结果的数目。未必是data.items数组的大小;如果我们查看的是最后一页,data.items的大小可能小于_itemsPerPage_。但是,data.items的大小不应超过_itemsPerPage_。
示例:
~~~
{
"data": {
"itemsPerPage": 10
}
}
~~~
### [](https://github.com/darcyliu/google-styleguide/blob/master/JSONStyleGuide.md#datastartindex)data.startIndex
~~~
属性值类型: 整数(integer)
父节点: data
~~~
data.items中第一个条目的索引。为了一致,_startIndex_应从1开始。例如,第一组items中第一条的_startIndex_应该是1。如果用户请求下一组数据,_startIndex_可能是10。
示例:
~~~
{
"data": {
"startIndex": 1
}
}
~~~
### [](https://github.com/darcyliu/google-styleguide/blob/master/JSONStyleGuide.md#datatotalitemsx)data.totalItemsx
~~~
属性值类型: 整数(integer)
父节点: data
~~~
当前集合中可用的总条目数。例如,如果用户有100篇博客文章,响应可能只包含10篇,但是_totalItems_应该是100。
示例:
~~~
{
"data": {
"totalItems": 100
}
}
~~~
### [](https://github.com/darcyliu/google-styleguide/blob/master/JSONStyleGuide.md#datapaginglinktemplate)data.pagingLinkTemplate
~~~
属性值类型: 字符串(string)
父节点: data
~~~
URL模板指出用户可以如何计算随后的分页链接。URL模板中也包含一些保留变量名:表示要载入的条目的_{index}_,和要载入的页面的_{pageIndex}_。
示例:
~~~
{
"data": {
"pagingLinkTemplate": "http://www.google.com/search/hl=en&q=chicago+style+pizza&start={index}&sa=N"
}
}
~~~
### [](https://github.com/darcyliu/google-styleguide/blob/master/JSONStyleGuide.md#datapageindex)data.pageIndex
~~~
属性值类型: 整数(integer)
父节点: data
~~~
条目的当前页索引。为了一致,_pageIndex_应从1开始。例如,第一页的_pageIndex_是1。_pageIndex_也可以通过基于条目的分页而计算出来_pageIndex = floor(startIndex / itemsPerPage) + 1_。
示例:
~~~
{
"data": {
"pageIndex": 1
}
}
~~~
### [](https://github.com/darcyliu/google-styleguide/blob/master/JSONStyleGuide.md#datatotalpages)data.totalPages
~~~
属性值类型: 整数(integer)
父节点: data
~~~
当前结果集中的总页数。_totalPages_也可以通过上面基于条目的分页属性计算出来: _totalPages = ceiling(totalItems / itemsPerPage)._。
示例:
~~~
{
"data": {
"totalPages": 50
}
}
~~~
data对象的保留属性名
最后更新于:2022-04-01 06:07:20
JSON对象的_data_属性可能包含以下属性。
### [](https://github.com/darcyliu/google-styleguide/blob/master/JSONStyleGuide.md#datakind)data.kind
~~~
属性值类型: 字符串(sting)
父节点: data
~~~
_kind_属性是对某个特定的对象存储何种类型的信息的指南。可以把它放在_data_层次,或_items_的层次,或其它任何有助于区分各类对象的对象中。如果_kind_对象被提供,它应该是对象的第一个属性(详见下面的_属性顺序_部分)。
示例:
~~~
// "Kind" indicates an "album" in the Picasa API.
{"data": {"kind": "album"}}
~~~
### [](https://github.com/darcyliu/google-styleguide/blob/master/JSONStyleGuide.md#datafields)data.fields
~~~
属性值类型: 字符串(string)
父节点: data
~~~
表示做了部分GET之后响应中出现的字段,或做了部分PATCH之后出现在请求中的字段。该属性仅在做了部分GET请求/批处理时存在,且不能为空。
示例:
~~~
{
"data": {
"kind": "user",
"fields": "author,id",
"id": "bart",
"author": "Bart"
}
}
~~~
### [](https://github.com/darcyliu/google-styleguide/blob/master/JSONStyleGuide.md#dataetag)data.etag
~~~
属性值类型: 字符串(string)
父节点: data
~~~
响应时提供etag。关于GData APIs中的ETags详情可以在这里找到:[http://code.google.com/apis/gdata/docs/2.0/reference.html#ResourceVersioning](http://code.google.com/apis/gdata/docs/2.0/reference.html#ResourceVersioning)
示例:
~~~
{"data": {"etag": "W/"C0QBRXcycSp7ImA9WxRVFUk.""}}
~~~
### [](https://github.com/darcyliu/google-styleguide/blob/master/JSONStyleGuide.md#dataid)data.id
~~~
属性值类型: 字符串(string)
父节点: data
~~~
一个全局唯一标识符用于引用该对象。_id_属性的具体细节都留给了服务。
示例:
~~~
{"data": {"id": "12345"}}
~~~
### [](https://github.com/darcyliu/google-styleguide/blob/master/JSONStyleGuide.md#datalang)data.lang
~~~
属性值类型: 字符串(string)(格式由BCP 47指定)
父节点: data (或任何子元素)
~~~
表示该对象内其他属性的语言。该属性模拟HTML的_lang_属性和XML的_xml:lang_属性。值应该时BCP 47中定义的一种语言值。如果一个单一的JSON对象包含的数据有多种语言,服务负责制定和标明的lang属性的适当位置。
示例:
~~~
{"data": {
"items": [
{ "lang": "en",
"title": "Hello world!" },
{ "lang": "fr",
"title": "Bonjour monde!" }
]}
}
~~~
### [](https://github.com/darcyliu/google-styleguide/blob/master/JSONStyleGuide.md#dataupdated)data.updated
~~~
属性值类型: 字符串(string)(格式由RFC 3339指定)
父节点: data
~~~
指明条目更新的最后日期/时间(RFC 3339),由服务规定。
示例:
~~~
{"data": {"updated": "2007-11-06T16:34:41.000Z"}}
~~~
### [](https://github.com/darcyliu/google-styleguide/blob/master/JSONStyleGuide.md#datadeleted)data.deleted
~~~
属性值类型: 布尔(boolean)
父节点: data (或任何子元素)
~~~
一个标记元素,当出现时,表示包含的条目已被删除。如果提供了删除属性,它的值必须为_true_;为_false_会导致混乱,应该避免。
示例:
~~~
{"data": {
"items": [
{ "title": "A deleted entry",
"deleted": true
}
]}
}
~~~
### [](https://github.com/darcyliu/google-styleguide/blob/master/JSONStyleGuide.md#dataitems)data.items
~~~
属性值类型: 数组(array)
父节点: data
~~~
属性名_items_被保留用作表示一组条目(例如,Picasa中的图片,YouTube中的视频)。这种结构的目的是给与当前结果相关的集合提供一个标准位置。例如,知道页面上的_items_是数组,JSON输出便可能插入一个通用的分页系统。如果_items_存在,它应该是_data_对象的最后一个属性。(详见下面的_属性顺序_部分)。
示例:
~~~
{
"data": {
"items": [
{ /* Object #1 */ },
{ /* Object #2 */ },
...
]
}
}
~~~
顶级保留属性名称
最后更新于:2022-04-01 06:07:18
**顶级的JSON对象可能包含下面这些属性**
### [](https://github.com/darcyliu/google-styleguide/blob/master/JSONStyleGuide.md#apiversion)apiVersion
~~~
属性值类型: 字符串(string)
父节点: -
~~~
呈现请求中服务API期望的版本,以及在响应中保存的服务API版本。应随时提供_apiVersion_。这与数据的版本无关。将数据版本化应该通过其他的机制来处理,如etag。
示例:
~~~
{ "apiVersion": "2.1" }
~~~
### [](https://github.com/darcyliu/google-styleguide/blob/master/JSONStyleGuide.md#context)context
~~~
属性值类型: 字符串(string)
父节点: -
~~~
客户端设置这个值,服务器通过数据作出回应。这在JSON-P和批处理中很有用,用户可以使用_context_将响应与请求关联起来。该属性是顶级属性,因为不管响应是成功还是有错误,_context_总应当被呈现出来。_context_不同于_id_在于_context_由用户提供而_id_由服务分配。
示例:
请求 #1:
~~~
http://www.google.com/myapi?context=bart
~~~
请求 #2:
~~~
http://www.google.com/myapi?context=lisa
~~~
响应 #1:
~~~
{
"context": "bart",
"data": {
"items": []
}
}
~~~
响应 #2:
~~~
{
"context": "lisa",
"data": {
"items": []
}
}
~~~
公共的JavaScript处理器通过编码同时处理以下两个响应:
~~~
function handleResponse(response) {
if (response.result.context == "bart") {
// 更新页面中的 "Bart" 部分。
} else if (response.result.context == "lisa") {
// 更新页面中的 "Lisa" 部分。
}
}
~~~
### [](https://github.com/darcyliu/google-styleguide/blob/master/JSONStyleGuide.md#id)id
~~~
属性值类型: 字符串(string)
父节点: -
~~~
服务提供用于识别响应的标识(无论请求是成功还是有错误)。这对于将服务日志和单独收到的响应对应起来很有用。
示例:
~~~
{ "id": "1" }
~~~
### [](https://github.com/darcyliu/google-styleguide/blob/master/JSONStyleGuide.md#method)method
~~~
属性值类型: 字符串(string)
父节点: -
~~~
表示对数据即将执行,或已被执行的操作。在JSON请求的情况下,_method_属性可以用来指明对数据进行何种操作。在JSON响应的情况下,_method_属性表明对数据进行了何种操作。
一个JSON-RPC请求的例子,其中_method_属性表示要在_params_上执行的操作:
~~~
{
"method": "people.get",
"params": {
"userId": "@me",
"groupId": "@self"
}
}
~~~
### [](https://github.com/darcyliu/google-styleguide/blob/master/JSONStyleGuide.md#params)params
~~~
属性值类型: 对象(object)
父节点: -
~~~
这个对象作为输入参数的映射发送给RPC请求。它可以和_method_属性一起用来执行RPC功能。若RPC方法不需要参数,则可以省略该属性。
示例:
~~~
{
"method": "people.get",
"params": {
"userId": "@me",
"groupId": "@self"
}
}
~~~
### [](https://github.com/darcyliu/google-styleguide/blob/master/JSONStyleGuide.md#data)data
~~~
属性值类型: 对象(object)
父节点: -
~~~
包含响应的所有数据。该属性本身拥有许多保留属性名,下面会有相应的说明。服务可以自由地将自己的数据添加到这个对象。一个JSON响应要么应当包含一个_data_对象,要么应当包含_error_对象,但不能两者都包含。如果_data_和_error_同时出现,则_error_对象优先。
### [](https://github.com/darcyliu/google-styleguide/blob/master/JSONStyleGuide.md#error)error
~~~
属性值类型: 对象(object)
父节点: -
~~~
表明错误发生,提供错误的详细信息。错误的格式支持从服务返回一个或多个错误。一个JSON响应可以有一个_data_对象或者一个_error_对象,但不能两者都包含。如果_data_和_error_都出现,_error_对象优先。
示例:
~~~
{
"apiVersion": "2.0",
"error": {
"code": 404,
"message": "File Not Found",
"errors": [{
"domain": "Calendar",
"reason": "ResourceNotFoundException",
"message": "File Not Found
}]
}
}
~~~
JSON结构和保留属性名
最后更新于:2022-04-01 06:07:15
为了使APIs保持一致的借口,JSON对象应当使用以下的结构。该结构适用于JSON的请求和响应。在这个结构中,某些属性名将被保留用作特殊用途。这些属性并不是必需的,也就是说,每个保留的属性可能出现零次或一次。但是如果服务需要这些属性,建议遵循该命名条约。下面是一份JSON结构语义表,以Orderly格式呈现(现在已经被纳入 JSONSchema)。你可以在该指南的最后找到关于JSON结构的例子。
~~~
object {
string apiVersion?;
string context?;
string id?;
string method?;
object {
string id?
}* params?;
object {
string kind?;
string fields?;
string etag?;
string id?;
string lang?;
string updated?; # date formatted RFC 3339
boolean deleted?;
integer currentItemCount?;
integer itemsPerPage?;
integer startIndex?;
integer totalItems?;
integer pageIndex?;
integer totalPages?;
string pageLinkTemplate /^https?:/ ?;
object {}* next?;
string nextLink?;
object {}* previous?;
string previousLink?;
object {}* self?;
string selfLink?;
object {}* edit?;
string editLink?;
array [
object {}*;
] items?;
}* data?;
object {
integer code?;
string message?;
array [
object {
string domain?;
string reason?;
string message?;
string location?;
string locationType?;
string extendedHelp?;
string sendReport?;
}*;
] errors?;
}* error?;
}*;
~~~
JSON对象有一些顶级属性,然后是_data_对象或_error_对象,这两者不会同时出现。下面是这些属性的解释。
属性值数据类型
最后更新于:2022-04-01 06:07:13
上面提到,属性值必须是布尔(booleans), 数字(numbers), 字符串(strings), 对象(objects), 数组(arrays), 或 null. 然而在处理某些值时,定义一组标准的数据类型是非常有用的。这些数据类型必须始终是字符串,但是为了便于解析,它们也会以特定的方式被格式化。
## 日期属性值
**日期应该使用RFC3339建议的格式**
日期应该是RFC 3339所建议的字符串格式。
~~~
{
"lastUpdate": "2007-11-06T16:34:41.000Z"
}
~~~
## 时间间隔属性值
**时间间隔应该使用ISO 8601建议的格式**
时间间隔应该是ISO 8601所建议的字符串格式。
~~~
{
// 三年, 6个月, 4天, 12小时,
// 三十分钟, 5秒
"duration": "P3Y6M4DT12H30M5S"
}
~~~
## 纬度/经度属性值
**纬度/经度应该使用ISO 6709建议的格式**
纬度/经度应该是ISO 6709所建议的字符串格式。 而且, 它应该更偏好使用 e ±DD.DDDD±DDD.DDDD 角度格式.
~~~
{
// 自由女神像的纬度/经度位置.
"statueOfLiberty": "+40.6894-074.0447"
}
~~~
属性值准则
最后更新于:2022-04-01 06:07:11
## 属性值格式
**属性值必须是Unicode 的 booleans(布尔), 数字(numbers), 字符串(strings), 对象(objects), 数组(arrays), 或 null.**
JSON.org上的标准准确的说明了哪些类型的数据可以作为属性值。这包含Unicode的布尔(booleans), 数字(numbers), 字符串(strings), 对象(objects), 数组(arrays), 或 null。JavaScript表达式是不被接受的。APIs应该支持该准则,并为某个特定的属性选择最合适的数据类型(比如,用numbers代表numbers等)。
好的例子:
~~~
{
"canPigsFly": null, // null
"areWeThereYet": false, // boolean
"answerToLife": 42, // number
"name": "Bart", // string
"moreData": {}, // object
"things": [] // array
}
~~~
不好的例子:
~~~
{
"aVariableName": aVariableName, // Bad - JavaScript 标识符
"functionFoo": function() { return 1; } // Bad - JavaScript 函数
}
~~~
## 空或Null 属性值
**考虑移除空或null值**
如果一个属性是可选的或者包含空值或_null_值,考虑从JSON中去掉该属性,除非它的存在有很强的语义原因。
~~~
{
"volume": 10,
// 即使 "balance" 属性值是零, 它也应当被保留,
// 因为 "0" 表示 "均衡"
// "-1" 表示左倾斜和"+1" 表示右倾斜
"balance": 0,
// "currentlyPlaying" 是null的时候可被移除
// "currentlyPlaying": null
}
~~~
## 枚举值
**枚举值应当以字符串的形式呈现**
随着APIs的发展,枚举值可能被添加,移除或者改变。将枚举值当作字符串可以使下游用户幽雅的处理枚举值的变更。
Java代码:
~~~
public enum Color {
WHITE,
BLACK,
RED,
YELLOW,
BLUE
}
~~~
JSON对象:
~~~
{
"color": "WHITE"
}
~~~
属性名准则
最后更新于:2022-04-01 06:07:08
## 属性名格式
**选择有意义的属性名**
属性名必须遵循以下准则:
* 属性名应该是具有定义语义的有意义的名称。
* 属性名必须是驼峰式的,ASCII码字符串。
* 首字符必须式字母,下划线(___)或美元符号(_$_)。
* 随后的其他字符可以是字母,数字,下划线(___)或美元符号(_$_)。
* 应该避免使用Javascript中的保留关键字(下文附有Javascript保留字清单)
这些准则反映JavaScript标识符命名的指导方针。使JavaScript的客户端可以使用点符号来访问属性。(例如,`result.thisIsAnInstanceVariable`).
下面是一个对象的一个属性的例子:
~~~
{
"thisPropertyIsAnIdentifier": "identifier value"
}
~~~
## JSON Map中的键名
**在JSON Map中键名可以使用任意Unicode字符**
当JSON对象作为Map(映射)使用时,属性的名称命名规则并不适用。Map(也称作关联数组)是一个具有任意键/值对的数据类型,这些键/值对通过特定的键来访问相应的值。JSON对象和JSON Map在运行时看起来是一样的;这个特性与API设计相关。当JSON对象被当作map使用时,API文件应当做出说明。
Map的键名不一定要遵循属性名称的命名准则。键名可以包含任意的Unicode字符。客户端可使用maps熟悉的方括号来访问这些属性。(例如`result.thumbnails["72"]`)
~~~
{
// "address" 属性是一个子对象
// 包含地址的各部分.
"address": {
"addressLine1": "123 Anystreet",
"city": "Anytown",
"state": "XX",
"zip": "00000"
},
// "address" 是一个映射
// 含有响应规格所对应的URL,用来映射thumbnail url的像素规格
"thumbnails": {
"72": "http://url.to.72px.thumbnail",
"144": "http://url.to.144px.thumbnail"
}
}
~~~
## 保留的属性名称
**某些属性名称会被保留以便能在多个服务间相容使用**
保留属性名称的详细信息,连同完整的列表,可在本指南后面的内容中找到。服务应按照被定义的语义来使用属性名称。
## 单数属性名 VS 复数属性名
**数组类型应该是复数属性名。其它属性名都应该是单数。**
数组通常包含多个条目,复数属性名就反映了这点。在下面这个保留名称中可以看到例子。属性名_items_是复数因为它描述的是一组对象。大多数的其它字段是单数。
当然也有例外,尤其是涉及到数字的属性值的时候。例如,在保留属性名中,_totalItems_ 比 _totalItem_更合理。然后,从技术上讲,这并不违反风格指南,因为 _totalItems_ 可以被看作 _totalOfItems_, 其中 _total_ 是单数(依照风格指南),_OfItems_ 用来限定总数。字段名也可被改为 _itemCount_,这样看起来更象单数.
~~~
{
// 单数
"author": "lisa",
// 一组同胞, 复数
"siblings": [ "bart", "maggie"],
// "totalItem" 看起来并不对
"totalItems": 10,
// 但 "itemCount" 要好些
"itemCount": 10,
}
~~~
## 命名冲突
**通过选择新的属性名或将API版本化来避免命名冲突**
新的属性可在将来被添加进保留列表中。JSON中不存在命名空间。如果存在命名冲突,可通过选择新的属性名或者版本化来解决这个问题。例如,假设我们由下面的JSON对象开始:
~~~
{
"apiVersion": "1.0",
"data": {
"recipeName": "pizza",
"ingredients": ["tomatoes", "cheese", "sausage"]
}
}
~~~
如果我们希望将来把_ingredients_列为保留字,我们可以通过下面两件事情来达成。 1.选一个不同的名字
~~~
{
"apiVersion": "1.0",
"data": {
"recipeName": "pizza",
"ingredientsData": "Some new property",
"ingredients": ["tomatoes", "cheese", "sausage"]
}
}
~~~
2.在主版本上重新命名属性
~~~
{
"apiVersion": "2.0",
"data": {
"recipeName": "pizza",
"ingredients": "Some new property",
"recipeIngredients": ["tomatos", "cheese", "sausage"]
}
}
~~~
一般准则
最后更新于:2022-04-01 06:07:06
## 注释
**JSON对象中不包含注释。**
JSON对象中不应该包含注释。该指南中的某些示例含有注释。但这仅仅是为了说明示例。
~~~
{
// 你可能在下面的示例中看到注释,
// 但不要在你的JSON数据中加入注释.
"propertyName": "propertyValue"
}
~~~
## 双引号
**使用双引号**
如果(某个)属性需要引号,则必须使用双引号。所有的属性名必须在双引号内。字符类型的属性值必须使用双引号。其它类型值(如布尔或数字)不应该使用双引号。
## 扁平化数据 VS 结构层次
**不能为了方便而将数据任意分组**
JSON中的数据元素应以_扁平化_方式呈现。不能为了方便而将数据任意分组。
在某些情况下,比如描述单一结构的一批属性,因为它被用来保持结构层次,因而是有意义的。但是遇到这些情况还是应当慎重考虑,记住只有语义上有意义的时候才使用它。例如,一个地址可以有表示两种方式,但结构化的方式对开发人员来讲可能更有意义:
扁平化地址:
~~~
{
"company": "Google",
"website": "http://www.google.com/",
"addressLine1": "111 8th Ave",
"addressLine2": "4th Floor",
"state": "NY",
"city": "New York",
"zip": "10011"
}
~~~
结构化地址:
~~~
{
"company": "Google",
"website": "http://www.google.com/",
"address": {
"line1": "111 8th Ave",
"line2": "4th Floor",
"state": "NY",
"city": "New York",
"zip": "10011"
}
}
~~~
定义
最后更新于:2022-04-01 06:07:04
为了更好地实现这份风格指南的目的,下面几项需要说明:
* 属性(property) - JSON对象内的键值对(name/value pair)
* 属性名(property name) - 属性的名称(或键)
* 属性值(property value) - 分配给属性的值
示例:
~~~
{
// 一组键值对称作一个 "属性".
"propertyName": "propertyValue"
}
~~~
Javascript的数字(_number_)包含所有的浮点数,这是一个宽泛的指定。在这份指南中,数字(_number_)指代Javascript中的数字(_number_)类型,而整型(_integer_)则指代整型。
简介
最后更新于:2022-04-01 06:07:02
该风格指南是对在Google创建JSON APIs而提供的指导性准则和建议。总体来讲,JSON APIs应遵循JSON.org上的规范。这份风格指南澄清和标准化了特定情况,从而使Google的JSON APIs有一种标准的外观和感觉。这些指南适用于基于RPC和基于REST风格的API的JSON请求和响应。
JSON 风格指南
最后更新于:2022-04-01 06:06:59
> 版本:0.9
> 英文版:http://google-styleguide.googlecode.com/svn/trunk/jsoncstyleguide.xml
> 翻译:Darcy Liu
临别赠言
最后更新于:2022-04-01 06:06:57
**请务必保持代码的一致性**
如果你正在编辑代码, 花几分钟看一下周边代码, 然后决定风格. 如果它们在所有的算术操作符两边都使用空格, 那么你也应该这样做. 如果它们的注释都用标记包围起来, 那么你的注释也要这样.
制定风格指南的目的在于让代码有规可循, 这样人们就可以专注于”你在说什么”, 而不是”你在怎么说”. 我们在这里给出的是全局的规范, 但是本地的规范同样重要. 如果你加到一个文件里的代码和原有代码大相径庭, 它会让读者不知所措. 避免这种情况.
> Revision 2.59
> Amit Patel
> Antoine Picard
> Eugene Jhong
> Gregory P. Smith
> Jeremy Hylton
> Matt Smart
> Mike Shields
> Shane Liebling
Python风格规范
最后更新于:2022-04-01 06:06:55
## 分号
> Tip
> 不要在行尾加分号, 也不要用分号将两条命令放在同一行.
## 行长度
> Tip
> 每行不超过80个字符
例外:
1. 长的导入模块语句
2. 注释里的URL
不要使用反斜杠连接行.
Python会将 [圆括号, 中括号和花括号中的行隐式的连接起来](http://docs.python.org/2/reference/lexical_analysis.html#implicit-line-joining) , 你可以利用这个特点. 如果需要, 你可以在表达式外围增加一对额外的圆括号.
~~~
Yes: foo_bar(self, width, height, color='black', design=None, x='foo',
emphasis=None, highlight=0)
if (width == 0 and height == 0 and
color == 'red' and emphasis == 'strong'):
~~~
如果一个文本字符串在一行放不下, 可以使用圆括号来实现隐式行连接:
~~~
x = ('This will build a very long long '
'long long long long long long string')
~~~
在注释中,如果必要,将长的URL放在一行上。
~~~
Yes: # See details at
# http://www.example.com/us/developer/documentation/api/content/v2.0/csv_file_name_extension_full_specification.html
~~~
~~~
No: # See details at
# http://www.example.com/us/developer/documentation/api/content/\
# v2.0/csv_file_name_extension_full_specification.html
~~~
注意上面例子中的元素缩进; 你可以在本文的 [缩进](http://zh-google-styleguide.readthedocs.org/en/latest/google-python-styleguide/python_style_rules/#indentation) 部分找到解释.
## 括号
> Tip
> 宁缺毋滥的使用括号
除非是用于实现行连接, 否则不要在返回语句或条件语句中使用括号. 不过在元组两边使用括号是可以的.
~~~
Yes: if foo:
bar()
while x:
x = bar()
if x and y:
bar()
if not x:
bar()
return foo
for (x, y) in dict.items(): ...
~~~
~~~
No: if (x):
bar()
if not(x):
bar()
return (foo)
~~~
## 缩进
> Tip
> 用4个空格来缩进代码
绝对不要用tab, 也不要tab和空格混用. 对于行连接的情况, 你应该要么垂直对齐换行的元素(见 [行长度](http://zh-google-styleguide.readthedocs.org/en/latest/google-python-styleguide/python_style_rules/#line-length) 部分的示例), 或者使用4空格的悬挂式缩进(这时第一行不应该有参数):
~~~
Yes: # Aligned with opening delimiter
foo = long_function_name(var_one, var_two,
var_three, var_four)
# Aligned with opening delimiter in a dictionary
foo = {
long_dictionary_key: value1 +
value2,
...
}
# 4-space hanging indent; nothing on first line
foo = long_function_name(
var_one, var_two, var_three,
var_four)
# 4-space hanging indent in a dictionary
foo = {
long_dictionary_key:
long_dictionary_value,
...
}
~~~
~~~
No: # Stuff on first line forbidden
foo = long_function_name(var_one, var_two,
var_three, var_four)
# 2-space hanging indent forbidden
foo = long_function_name(
var_one, var_two, var_three,
var_four)
# No hanging indent in a dictionary
foo = {
long_dictionary_key:
long_dictionary_value,
...
}
~~~
## 空行
> Tip
> 顶级定义之间空两行, 方法定义之间空一行
顶级定义之间空两行, 比如函数或者类定义. 方法定义, 类定义与第一个方法之间, 都应该空一行. 函数或方法中, 某些地方要是你觉得合适, 就空一行.
## 空格
> Tip
> 按照标准的排版规范来使用标点两边的空格
括号内不要有空格.
~~~
Yes: spam(ham[1], {eggs: 2}, [])
~~~
~~~
No: spam( ham[ 1 ], { eggs: 2 }, [ ] )
~~~
不要在逗号, 分号, 冒号前面加空格, 但应该在它们后面加(除了在行尾).
~~~
Yes: if x == 4:
print x, y
x, y = y, x
~~~
~~~
No: if x == 4 :
print x , y
x , y = y , x
~~~
参数列表, 索引或切片的左括号前不应加空格.
~~~
Yes: spam(1)
~~~
~~~
no: spam (1)
~~~
~~~
Yes: dict['key'] = list[index]
~~~
~~~
No: dict ['key'] = list [index]
~~~
在二元操作符两边都加上一个空格, 比如赋值(=), 比较(==, , !=, =, in, not in, is, is not), 布尔(and, or, not). 至于算术操作符两边的空格该如何使用, 需要你自己好好判断. 不过两侧务必要保持一致.
~~~
Yes: x == 1
~~~
~~~
No: x1
~~~
当’=’用于指示关键字参数或默认参数值时, 不要在其两侧使用空格.
~~~
Yes: def complex(real, imag=0.0): return magic(r=real, i=imag)
~~~
~~~
No: def complex(real, imag = 0.0): return magic(r = real, i = imag)
~~~
不要用空格来垂直对齐多行间的标记, 因为这会成为维护的负担(适用于:, #, =等):
~~~
Yes:
foo = 1000 # comment
long_name = 2 # comment that should not be aligned
dictionary = {
"foo": 1,
"long_name": 2,
}
~~~
~~~
No:
foo = 1000 # comment
long_name = 2 # comment that should not be aligned
dictionary = {
"foo" : 1,
"long_name": 2,
}
~~~
## Shebang
> Tip
> 大部分.py文件不必以#!作为文件的开始. 根据 [PEP-394](http://www.python.org/dev/peps/pep-0394/) , 程序的main文件应该以 #!/usr/bin/python2或者 #!/usr/bin/python3开始.
(译者注: 在计算机科学中, [Shebang](http://en.wikipedia.org/wiki/Shebang_(Unix)) (也称为Hashbang)是一个由井号和叹号构成的字符串行(#!), 其出现在文本文件的第一行的前两个字符. 在文件中存在Shebang的情况下, 类Unix操作系统的程序载入器会分析Shebang后的内容, 将这些内容作为解释器指令, 并调用该指令, 并将载有Shebang的文件路径作为该解释器的参数. 例如, 以指令#!/bin/sh开头的文件在执行时会实际调用/bin/sh程序.)
先用于帮助内核找到Python解释器, 但是在导入模块时, 将会被忽略. 因此只有被直接执行的文件中才有必要加入.
## 注释
> Tip
> 确保对模块, 函数, 方法和行内注释使用正确的风格
**文档字符串**
> Python有一种独一无二的的注释方式: 使用文档字符串. 文档字符串是包, 模块, 类或函数里的第一个语句. 这些字符串可以通过对象的__doc__成员被自动提取, 并且被pydoc所用. (你可以在你的模块上运行pydoc试一把, 看看它长什么样). 我们对文档字符串的惯例是使用三重双引号”“”( [PEP-257](http://www.python.org/dev/peps/pep-0257/) ). 一个文档字符串应该这样组织: 首先是一行以句号, 问号或惊叹号结尾的概述(或者该文档字符串单纯只有一行). 接着是一个空行. 接着是文档字符串剩下的部分, 它应该与文档字符串的第一行的第一个引号对齐. 下面有更多文档字符串的格式化规范.
**模块**
> 每个文件应该包含一个许可样板. 根据项目使用的许可(例如, Apache 2.0, BSD, LGPL, GPL), 选择合适的样板.
**函数和方法**
下文所指的函数,包括函数, 方法, 以及生成器.
一个函数必须要有文档字符串, 除非它满足以下条件:
1. 外部不可见
2. 非常短小
3. 简单明了
文档字符串应该包含函数做什么, 以及输入和输出的详细描述. 通常, 不应该描述”怎么做”, 除非是一些复杂的算法. 文档字符串应该提供足够的信息, 当别人编写代码调用该函数时, 他不需要看一行代码, 只要看文档字符串就可以了. 对于复杂的代码, 在代码旁边加注释会比使用文档字符串更有意义.
关于函数的几个方面应该在特定的小节中进行描述记录, 这几个方面如下文所述. 每节应该以一个标题行开始. 标题行以冒号结尾. 除标题行外, 节的其他内容应被缩进2个空格.
Args:
列出每个参数的名字, 并在名字后使用一个冒号和一个空格, 分隔对该参数的描述.如果描述太长超过了单行80字符,使用2或者4个空格的悬挂缩进(与文件其他部分保持一致). 描述应该包括所需的类型和含义. 如果一个函数接受*foo(可变长度参数列表)或者**bar (任意关键字参数), 应该详细列出*foo和**bar.
Returns: (或者 Yields: 用于生成器)
描述返回值的类型和语义. 如果函数返回None, 这一部分可以省略.
Raises:
列出与接口有关的所有异常.
~~~
def fetch_bigtable_rows(big_table, keys, other_silly_variable=None):
"""Fetches rows from a Bigtable.
Retrieves rows pertaining to the given keys from the Table instance
represented by big_table. Silly things may happen if
other_silly_variable is not None.
Args:
big_table: An open Bigtable Table instance.
keys: A sequence of strings representing the key of each table row
to fetch.
other_silly_variable: Another optional variable, that has a much
longer name than the other args, and which does nothing.
Returns:
A dict mapping keys to the corresponding table row data
fetched. Each row is represented as a tuple of strings. For
example:
{'Serak': ('Rigel VII', 'Preparer'),
'Zim': ('Irk', 'Invader'),
'Lrrr': ('Omicron Persei 8', 'Emperor')}
If a key from the keys argument is missing from the dictionary,
then that row was not found in the table.
Raises:
IOError: An error occurred accessing the bigtable.Table object.
"""
pass
~~~
**类**
类应该在其定义下有一个用于描述该类的文档字符串. 如果你的类有公共属性(Attributes), 那么文档中应该有一个属性(Attributes)段. 并且应该遵守和函数参数相同的格式.
~~~
class SampleClass(object):
"""Summary of class here.
Longer class information....
Longer class information....
Attributes:
likes_spam: A boolean indicating if we like SPAM or not.
eggs: An integer count of the eggs we have laid.
"""
def __init__(self, likes_spam=False):
"""Inits SampleClass with blah."""
self.likes_spam = likes_spam
self.eggs = 0
def public_method(self):
"""Performs operation blah."""
~~~
**块注释和行注释**
最需要写注释的是代码中那些技巧性的部分. 如果你在下次 [代码审查](http://en.wikipedia.org/wiki/Code_review) 的时候必须解释一下, 那么你应该现在就给它写注释. 对于复杂的操作, 应该在其操作开始前写上若干行注释. 对于不是一目了然的代码, 应在其行尾添加注释.
~~~
# We use a weighted dictionary search to find out where i is in
# the array. We extrapolate position based on the largest num
# in the array and the array size and then do binary search to
# get the exact number.
if i & (i-1) == 0: # true iff i is a power of 2
~~~
为了提高可读性, 注释应该至少离开代码2个空格.
另一方面, 绝不要描述代码. 假设阅读代码的人比你更懂Python, 他只是不知道你的代码要做什么.
~~~
# BAD COMMENT: Now go through the b array and make sure whenever i occurs
# the next element is i+1
~~~
## 类
> Tip
> 如果一个类不继承自其它类, 就显式的从object继承. 嵌套类也一样.
~~~
Yes: class SampleClass(object):
pass
class OuterClass(object):
class InnerClass(object):
pass
class ChildClass(ParentClass):
"""Explicitly inherits from another class already."""
~~~
~~~
No: class SampleClass:
pass
class OuterClass:
class InnerClass:
pass
~~~
继承自 `object` 是为了使属性(properties)正常工作, 并且这样可以保护你的代码, 使其不受Python 3000的一个特殊的潜在不兼容性影响. 这样做也定义了一些特殊的方法, 这些方法实现了对象的默认语义, 包括 `__new__, __init__, __delattr__, __getattribute__, __setattr__, __hash__, __repr__, and __str__` .
## 字符串
> Tip
> 即使参数都是字符串, 使用%操作符或者格式化方法格式化字符串. 不过也不能一概而论, 你需要在+和%之间好好判定.
~~~
Yes: x = a + b
x = '%s, %s!' % (imperative, expletive)
x = '{}, {}!'.format(imperative, expletive)
x = 'name: %s; score: %d' % (name, n)
x = 'name: {}; score: {}'.format(name, n)
~~~
~~~
No: x = '%s%s' % (a, b) # use + in this case
x = '{}{}'.format(a, b) # use + in this case
x = imperative + ', ' + expletive + '!'
x = 'name: ' + name + '; score: ' + str(n)
~~~
避免在循环中用+和+=操作符来累加字符串. 由于字符串是不可变的, 这样做会创建不必要的临时对象, 并且导致二次方而不是线性的运行时间. 作为替代方案, 你可以将每个子串加入列表, 然后在循环结束后用 `.join` 连接列表. (也可以将每个子串写入一个 `cStringIO.StringIO` 缓存中.)
~~~
Yes: items = ['']
for last_name, first_name in employee_list:
items.append('%s, %s' % (last_name, first_name))
items.append('')
employee_table = ''.join(items)
~~~
~~~
No: employee_table = ''
for last_name, first_name in employee_list:
employee_table += '%s, %s' % (last_name, first_name)
employee_table += ''
~~~
在同一个文件中, 保持使用字符串引号的一致性. 使用单引号’或者双引号”之一用以引用字符串, 并在同一文件中沿用. 在字符串内可以使用另外一种引号, 以避免在字符串中使用. GPyLint已经加入了这一检查.
(译者注:GPyLint疑为笔误, 应为PyLint.)
~~~
Yes:
Python('Why are you hiding your eyes?')
Gollum("I'm scared of lint errors.")
Narrator('"Good!" thought a happy Python reviewer.')
~~~
~~~
No:
Python("Why are you hiding your eyes?")
Gollum('The lint. It burns. It burns us.')
Gollum("Always the great lint. Watching. Watching.")
~~~
为多行字符串使用三重双引号”“”而非三重单引号’‘’. 当且仅当项目中使用单引号’来引用字符串时, 才可能会使用三重’‘’为非文档字符串的多行字符串来标识引用. 文档字符串必须使用三重双引号”“”. 不过要注意, 通常用隐式行连接更清晰, 因为多行字符串与程序其他部分的缩进方式不一致.
~~~
Yes:
print ("This is much nicer.\n"
"Do it this way.\n")
~~~
~~~
No:
print """This is pretty ugly.
Don't do this.
"""
~~~
## 文件和sockets
> Tip
> 在文件和sockets结束时, 显式的关闭它.
除文件外, sockets或其他类似文件的对象在没有必要的情况下打开, 会有许多副作用, 例如:
1. 它们可能会消耗有限的系统资源, 如文件描述符. 如果这些资源在使用后没有及时归还系统, 那么用于处理这些对象的代码会将资源消耗殆尽.
2. 持有文件将会阻止对于文件的其他诸如移动、删除之类的操作.
3. 仅仅是从逻辑上关闭文件和sockets, 那么它们仍然可能会被其共享的程序在无意中进行读或者写操作. 只有当它们真正被关闭后, 对于它们尝试进行读或者写操作将会跑出异常, 并使得问题快速显现出来.
而且, 幻想当文件对象析构时, 文件和sockets会自动关闭, 试图将文件对象的生命周期和文件的状态绑定在一起的想法, 都是不现实的. 因为有如下原因:
1. 没有任何方法可以确保运行环境会真正的执行文件的析构. 不同的Python实现采用不同的内存管理技术, 比如延时垃圾处理机制. 延时垃圾处理机制可能会导致对象生命周期被任意无限制的延长.
2. 对于文件意外的引用,会导致对于文件的持有时间超出预期(比如对于异常的跟踪, 包含有全局变量等).
推荐使用 [“with”语句](http://docs.python.org/reference/compound_stmts.html#the-with-statement) 以管理文件:
~~~
with open("hello.txt") as hello_file:
for line in hello_file:
print line
~~~
对于不支持使用”with”语句的类似文件的对象,使用 contextlib.closing():
~~~
import contextlib
with contextlib.closing(urllib.urlopen("http://www.python.org/")) as front_page:
for line in front_page:
print line
~~~
Legacy AppEngine 中Python 2.5的代码如使用”with”语句, 需要添加 “from __future__ import with_statement”.
## TODO注释
> Tip
> 为临时代码使用TODO注释, 它是一种短期解决方案. 不算完美, 但够好了.
TODO注释应该在所有开头处包含”TODO”字符串, 紧跟着是用括号括起来的你的名字, email地址或其它标识符. 然后是一个可选的冒号. 接着必须有一行注释, 解释要做什么. 主要目的是为了有一个统一的TODO格式, 这样添加注释的人就可以搜索到(并可以按需提供更多细节). 写了TODO注释并不保证写的人会亲自解决问题. 当你写了一个TODO, 请注上你的名字.
~~~
# TODO(kl@gmail.com): Use a "*" here for string repetition.
# TODO(Zeke) Change this to use relations.
~~~
如果你的TODO是”将来做某事”的形式, 那么请确保你包含了一个指定的日期(“2009年11月解决”)或者一个特定的事件(“等到所有的客户都可以处理XML请求就移除这些代码”).
## 导入格式
> Tip
> 每个导入应该独占一行
~~~
Yes: import os
import sys
~~~
~~~
No: import os, sys
~~~
导入总应该放在文件顶部, 位于模块注释和文档字符串之后, 模块全局变量和常量之前. 导入应该按照从最通用到最不通用的顺序分组:
1. 标准库导入
2. 第三方库导入
3. 应用程序指定导入
每种分组中, 应该根据每个模块的完整包路径按字典序排序, 忽略大小写.
~~~
import foo
from foo import bar
from foo.bar import baz
from foo.bar import Quux
from Foob import ar
~~~
## 语句
> Tip
> 通常每个语句应该独占一行
不过, 如果测试结果与测试语句在一行放得下, 你也可以将它们放在同一行. 如果是if语句, 只有在没有else时才能这样做. 特别地, 绝不要对 `try/except` 这样做, 因为try和except不能放在同一行.
~~~
Yes:
if foo: bar(foo)
~~~
~~~
No:
if foo: bar(foo)
else: baz(foo)
try: bar(foo)
except ValueError: baz(foo)
try:
bar(foo)
except ValueError: baz(foo)
~~~
## 访问控制
> Tip
> 在Python中, 对于琐碎又不太重要的访问函数, 你应该直接使用公有变量来取代它们, 这样可以避免额外的函数调用开销. 当添加更多功能时, 你可以用属性(property)来保持语法的一致性.
(译者注: 重视封装的面向对象程序员看到这个可能会很反感, 因为他们一直被教育: 所有成员变量都必须是私有的! 其实, 那真的是有点麻烦啊. 试着去接受Pythonic哲学吧)
另一方面, 如果访问更复杂, 或者变量的访问开销很显著, 那么你应该使用像 `get_foo()` 和 `set_foo()` 这样的函数调用. 如果之前的代码行为允许通过属性(property)访问 , 那么就不要将新的访问函数与属性绑定. 这样, 任何试图通过老方法访问变量的代码就没法运行, 使用者也就会意识到复杂性发生了变化.
## 命名
> Tip
> module_name, package_name, ClassName, method_name, ExceptionName, function_name, GLOBAL_VAR_NAME, instance_var_name, function_parameter_name, local_var_name.
**应该避免的名称**
> 1. 单字符名称, 除了计数器和迭代器.
> 2. 包/模块名中的连字符(-)
> 3. 双下划线开头并结尾的名称(Python保留, 例如__init__)
**命名约定**
> 1. 所谓”内部(Internal)”表示仅模块内可用, 或者, 在类内是保护或私有的.
> 2. 用单下划线(_)开头表示模块变量或函数是protected的(使用import * from时不会包含).
> 3. 用双下划线(__)开头的实例变量或方法表示类内私有.
> 4. 将相关的类和顶级函数放在同一个模块里. 不像Java, 没必要限制一个类一个模块.
> 5. 对类名使用大写字母开头的单词(如CapWords, 即Pascal风格), 但是模块名应该用小写加下划线的方式(如lower_with_under.py). 尽管已经有很多现存的模块使用类似于CapWords.py这样的命名, 但现在已经不鼓励这样做, 因为如果模块名碰巧和类名一致, 这会让人困扰.
**Python之父Guido推荐的规范**
| Type | Public | Internal |
| --- | --- | --- |
| Modules | lower_with_under | _lower_with_under |
| Packages | lower_with_under |
| Classes | CapWords | _CapWords |
| Exceptions | CapWords |
| Functions | lower_with_under() | _lower_with_under() |
| Global/Class Constants | CAPS_WITH_UNDER | _CAPS_WITH_UNDER |
| Global/Class Variables | lower_with_under | _lower_with_under |
| Instance Variables | lower_with_under | _lower_with_under (protected) or __lower_with_under (private) |
| Method Names | lower_with_under() | _lower_with_under() (protected) or __lower_with_under() (private) |
| Function/Method Parameters | lower_with_under |
| Local Variables | lower_with_under |
## Main
> Tip
> 即使是一个打算被用作脚本的文件, 也应该是可导入的. 并且简单的导入不应该导致这个脚本的主功能(main functionality)被执行, 这是一种副作用. 主功能应该放在一个main()函数中.
在Python中, pydoc以及单元测试要求模块必须是可导入的. 你的代码应该在执行主程序前总是检查 `if __name__ == '__main__'` , 这样当模块被导入时主程序就不会被执行.
~~~
def main():
...
if __name__ == '__main__':
main()
~~~
所有的顶级代码在模块导入时都会被执行. 要小心不要去调用函数, 创建对象, 或者执行那些不应该在使用pydoc时执行的操作.
Python语言规范
最后更新于:2022-04-01 06:06:52
## Lint
> Tip
> 对你的代码运行pylint
定义:
pylint是一个在Python源代码中查找bug的工具. 对于C和C++这样的不那么动态的(译者注: 原文是less dynamic)语言, 这些bug通常由编译器来捕获. 由于Python的动态特性, 有些警告可能不对. 不过伪告警应该很少.
优点:
可以捕获容易忽视的错误, 例如输入错误, 使用未赋值的变量等.
缺点:
pylint不完美. 要利用其优势, 我们有时侯需要: a) 围绕着它来写代码 b) 抑制其告警 c) 改进它, 或者d) 忽略它.
结论:
确保对你的代码运行pylint.抑制不准确的警告,以便能够将其他警告暴露出来。
你可以通过设置一个行注释来抑制告警. 例如:
~~~
dict = 'something awful' # Bad Idea... pylint: disable=redefined-builtin
~~~
pylint警告是以一个数字编号(如 `C0112` )和一个符号名(如 `empty-docstring` )来标识的. 在编写新代码或更新已有代码时对告警进行医治, 推荐使用符号名来标识.
如果警告的符号名不够见名知意,那么请对其增加一个详细解释。
采用这种抑制方式的好处是我们可以轻松查找抑制并回顾它们.
你可以使用命令 `pylint --list-msgs` 来获取pylint告警列表. 你可以使用命令 `pylint --help-msg=C6409` , 以获取关于特定消息的更多信息.
相比较于之前使用的 `pylint: disable-msg` , 本文推荐使用 `pylint: disable` .
要抑制”参数未使用”告警, 你可以用”_”作为参数标识符, 或者在参数名前加”unused_”. 遇到不能改变参数名的情况, 你可以通过在函数开头”提到”它们来消除告警. 例如:
~~~
def foo(a, unused_b, unused_c, d=None, e=None):
_ = d, e
return a
~~~
## 导入
> Tip
> 仅对包和模块使用导入
定义:
模块间共享代码的重用机制.
优点:
命名空间管理约定十分简单. 每个标识符的源都用一种一致的方式指示. x.Obj表示Obj对象定义在模块x中.
缺点:
模块名仍可能冲突. 有些模块名太长, 不太方便.
结论:
使用 `import x` 来导入包和模块.
使用 `from x import y` , 其中x是包前缀, y是不带前缀的模块名.
使用 `from x import y as z`, 如果两个要导入的模块都叫做z或者y太长了.
例如, 模块 `sound.effects.echo` 可以用如下方式导入:
~~~
from sound.effects import echo
...
echo.EchoFilter(input, output, delay=0.7, atten=4)
~~~
导入时不要使用相对名称. 即使模块在同一个包中, 也要使用完整包名. 这能帮助你避免无意间导入一个包两次.
## 包
> Tip
> 使用模块的全路径名来导入每个模块
优点:
避免模块名冲突. 查找包更容易.
缺点:
部署代码变难, 因为你必须复制包层次.
结论:
所有的新代码都应该用完整包名来导入每个模块.
应该像下面这样导入:
~~~
# Reference in code with complete name.
import sound.effects.echo
# Reference in code with just module name (preferred).
from sound.effects import echo
~~~
## 异常
> Tip
> 允许使用异常, 但必须小心
定义:
异常是一种跳出代码块的正常控制流来处理错误或者其它异常条件的方式.
优点:
正常操作代码的控制流不会和错误处理代码混在一起. 当某种条件发生时, 它也允许控制流跳过多个框架. 例如, 一步跳出N个嵌套的函数, 而不必继续执行错误的代码.
缺点:
可能会导致让人困惑的控制流. 调用库时容易错过错误情况.
结论:
异常必须遵守特定条件:
1. 像这样触发异常: `raise MyException("Error message")` 或者 `raise MyException` . 不要使用两个参数的形式( `raise MyException, "Error message"` )或者过时的字符串异常(`raise "Error message"` ).
2. 模块或包应该定义自己的特定域的异常基类, 这个基类应该从内建的Exception类继承. 模块的异常基类应该叫做”Error”.
~~~
class Error(Exception):
pass
~~~
3. 永远不要使用 `except:` 语句来捕获所有异常, 也不要捕获 `Exception` 或者 `StandardError` , 除非你打算重新触发该异常, 或者你已经在当前线程的最外层(记得还是要打印一条错误消息). 在异常这方面, Python非常宽容, `except:` 真的会捕获包括Python语法错误在内的任何错误. 使用 `except:` 很容易隐藏真正的bug.
4. 尽量减少try/except块中的代码量. try块的体积越大, 期望之外的异常就越容易被触发. 这种情况下, try/except块将隐藏真正的错误.
5. 使用finally子句来执行那些无论try块中有没有异常都应该被执行的代码. 这对于清理资源常常很有用, 例如关闭文件.
6. 当捕获异常时, 使用 `as` 而不要用逗号. 例如
~~~
try:
raise Error
except Error as error:
pass
~~~
## 全局变量
> Tip
> 避免全局变量
定义:
定义在模块级的变量.
优点:
偶尔有用.
缺点:
导入时可能改变模块行为, 因为导入模块时会对模块级变量赋值.
结论:
避免使用全局变量, 用类变量来代替. 但也有一些例外:
1. 脚本的默认选项.
2. 模块级常量. 例如: PI = 3.14159\. 常量应该全大写, 用下划线连接.
3. 有时候用全局变量来缓存值或者作为函数返回值很有用.
4. 如果需要, 全局变量应该仅在模块内部可用, 并通过模块级的公共函数来访问.
## 嵌套/局部/内部类或函数
> Tip
> 鼓励使用嵌套/本地/内部类或函数
定义:
类可以定义在方法, 函数或者类中. 函数可以定义在方法或函数中. 封闭区间中定义的变量对嵌套函数是只读的.
优点:
允许定义仅用于有效范围的工具类和函数.
缺点:
嵌套类或局部类的实例不能序列化(pickled).
结论:
推荐使用.
## 列表推导(List Comprehensions)
> Tip
> 可以在简单情况下使用
定义:
列表推导(list comprehensions)与生成器表达式(generator expression)提供了一种简洁高效的方式来创建列表和迭代器, 而不必借助map(), filter(), 或者lambda.
优点:
简单的列表推导可以比其它的列表创建方法更加清晰简单. 生成器表达式可以十分高效, 因为它们避免了创建整个列表.
缺点:
复杂的列表推导或者生成器表达式可能难以阅读.
结论:
适用于简单情况. 每个部分应该单独置于一行: 映射表达式, for语句, 过滤器表达式. 禁止多重for语句或过滤器表达式. 复杂情况下还是使用循环.
~~~
Yes:
result = []
for x in range(10):
for y in range(5):
if x * y > 10:
result.append((x, y))
for x in xrange(5):
for y in xrange(5):
if x != y:
for z in xrange(5):
if y != z:
yield (x, y, z)
return ((x, complicated_transform(x))
for x in long_generator_function(parameter)
if x is not None)
squares = [x * x for x in range(10)]
eat(jelly_bean for jelly_bean in jelly_beans
if jelly_bean.color == 'black')
~~~
~~~
No:
result = [(x, y) for x in range(10) for y in range(5) if x * y > 10]
return ((x, y, z)
for x in xrange(5)
for y in xrange(5)
if x != y
for z in xrange(5)
if y != z)
~~~
## 默认迭代器和操作符
> Tip
> 如果类型支持, 就使用默认迭代器和操作符. 比如列表, 字典及文件等.
定义:
容器类型, 像字典和列表, 定义了默认的迭代器和关系测试操作符(in和not in)
优点:
默认操作符和迭代器简单高效, 它们直接表达了操作, 没有额外的方法调用. 使用默认操作符的函数是通用的. 它可以用于支持该操作的任何类型.
缺点:
你没法通过阅读方法名来区分对象的类型(例如, has_key()意味着字典). 不过这也是优点.
结论:
如果类型支持, 就使用默认迭代器和操作符, 例如列表, 字典和文件. 内建类型也定义了迭代器方法. 优先考虑这些方法, 而不是那些返回列表的方法. 当然,这样遍历容器时,你将不能修改容器.
~~~
Yes: for key in adict: ...
if key not in adict: ...
if obj in alist: ...
for line in afile: ...
for k, v in dict.iteritems(): ...
~~~
~~~
No: for key in adict.keys(): ...
if not adict.has_key(key): ...
for line in afile.readlines(): ...
~~~
## 生成器
> Tip
> 按需使用生成器.
定义:
所谓生成器函数, 就是每当它执行一次生成(yield)语句, 它就返回一个迭代器, 这个迭代器生成一个值. 生成值后, 生成器函数的运行状态将被挂起, 直到下一次生成.
优点:
简化代码, 因为每次调用时, 局部变量和控制流的状态都会被保存. 比起一次创建一系列值的函数, 生成器使用的内存更少.
缺点:
没有.
结论:
鼓励使用. 注意在生成器函数的文档字符串中使用”Yields:”而不是”Returns:”.
(译者注: 参看 [注释](http://zh-google-styleguide.readthedocs.org/en/latest/google-python-styleguide/python_style_rules/#comments) )
## Lambda函数
> Tip
> 适用于单行函数
定义:
与语句相反, lambda在一个表达式中定义匿名函数. 常用于为 `map()` 和 `filter()` 之类的高阶函数定义回调函数或者操作符.
优点:
方便.
缺点:
比本地函数更难阅读和调试. 没有函数名意味着堆栈跟踪更难理解. 由于lambda函数通常只包含一个表达式, 因此其表达能力有限.
结论:
适用于单行函数. 如果代码超过60-80个字符, 最好还是定义成常规(嵌套)函数.
对于常见的操作符,例如乘法操作符,使用 `operator` 模块中的函数以代替lambda函数. 例如, 推荐使用 `operator.mul` , 而不是 `lambda x, y: x * y` .
## 条件表达式
> Tip
> 适用于单行函数
定义:
条件表达式是对于if语句的一种更为简短的句法规则. 例如: `x = 1 if cond else 2` .
优点:
比if语句更加简短和方便.
缺点:
比if语句难于阅读. 如果表达式很长, 难于定位条件.
结论:
适用于单行函数. 在其他情况下,推荐使用完整的if语句.
## 默认参数值
> Tip
> 适用于大部分情况.
定义:
你可以在函数参数列表的最后指定变量的值, 例如, `def foo(a, b = 0):` . 如果调用foo时只带一个参数, 则b被设为0\. 如果带两个参数, 则b的值等于第二个参数.
优点:
你经常会碰到一些使用大量默认值的函数, 但偶尔(比较少见)你想要覆盖这些默认值. 默认参数值提供了一种简单的方法来完成这件事, 你不需要为这些罕见的例外定义大量函数. 同时, Python也不支持重载方法和函数, 默认参数是一种”仿造”重载行为的简单方式.
缺点:
默认参数只在模块加载时求值一次. 如果参数是列表或字典之类的可变类型, 这可能会导致问题. 如果函数修改了对象(例如向列表追加项), 默认值就被修改了.
结论:
鼓励使用, 不过有如下注意事项:
不要在函数或方法定义中使用可变对象作为默认值.
~~~
Yes: def foo(a, b=None):
if b is None:
b = []
~~~
~~~
No: def foo(a, b=[]):
...
No: def foo(a, b=time.time()): # The time the module was loaded???
...
No: def foo(a, b=FLAGS.my_thing): # sys.argv has not yet been parsed...
...
~~~
## 属性(properties)
> Tip
> 访问和设置数据成员时, 你通常会使用简单, 轻量级的访问和设置函数. 建议用属性(properties)来代替它们.
定义:
一种用于包装方法调用的方式. 当运算量不大, 它是获取和设置属性(attribute)的标准方式.
优点:
通过消除简单的属性(attribute)访问时显式的get和set方法调用, 可读性提高了. 允许懒惰的计算. 用Pythonic的方式来维护类的接口. 就性能而言, 当直接访问变量是合理的, 添加访问方法就显得琐碎而无意义. 使用属性(properties)可以绕过这个问题. 将来也可以在不破坏接口的情况下将访问方法加上.
缺点:
属性(properties)是在get和set方法声明后指定, 这需要使用者在接下来的代码中注意: set和get是用于属性(properties)的(除了用 `@property` 装饰器创建的只读属性). 必须继承自object类. 可能隐藏比如操作符重载之类的副作用. 继承时可能会让人困惑.
结论:
你通常习惯于使用访问或设置方法来访问或设置数据, 它们简单而轻量. 不过我们建议你在新的代码中使用属性. 只读属性应该用 `@property` [装饰器](http://google-styleguide.googlecode.com/svn/trunk/pyguide.html#Function_and_Method_Decorators) 来创建.
如果子类没有覆盖属性, 那么属性的继承可能看上去不明显. 因此使用者必须确保访问方法间接被调用, 以保证子类中的重载方法被属性调用(使用模板方法设计模式).
~~~
Yes: import math
class Square(object):
"""A square with two properties: a writable area and a read-only perimeter.
To use:
>>> sq = Square(3)
>>> sq.area
9
>>> sq.perimeter
12
>>> sq.area = 16
>>> sq.side
4
>>> sq.perimeter
16
"""
def __init__(self, side):
self.side = side
def __get_area(self):
"""Calculates the 'area' property."""
return self.side ** 2
def ___get_area(self):
"""Indirect accessor for 'area' property."""
return self.__get_area()
def __set_area(self, area):
"""Sets the 'area' property."""
self.side = math.sqrt(area)
def ___set_area(self, area):
"""Indirect setter for 'area' property."""
self._SetArea(area)
area = property(___get_area, ___set_area,
doc="""Gets or sets the area of the square.""")
@property
def perimeter(self):
return self.side * 4
~~~
(译者注: 老实说, 我觉得这段示例代码很不恰当, 有必要这么蛋疼吗?)
## True/False的求值
> Tip
> 尽可能使用隐式false
定义:
Python在布尔上下文中会将某些值求值为false. 按简单的直觉来讲, 就是所有的”空”值都被认为是false. 因此0, None, [], {}, “” 都被认为是false.
优点:
使用Python布尔值的条件语句更易读也更不易犯错. 大部分情况下, 也更快.
缺点:
对C/C++开发人员来说, 可能看起来有点怪.
结论:
尽可能使用隐式的false, 例如: 使用 `if foo:` 而不是 `if foo != []:` . 不过还是有一些注意事项需要你铭记在心:
1. 永远不要用==或者!=来比较单件, 比如None. 使用is或者is not.
2. 注意: 当你写下 `if x:` 时, 你其实表示的是 `if x is not None` . 例如: 当你要测试一个默认值是None的变量或参数是否被设为其它值. 这个值在布尔语义下可能是false!
3. 永远不要用==将一个布尔量与false相比较. 使用 `if not x:` 代替. 如果你需要区分false和None, 你应该用像 `if not x and x is not None:` 这样的语句.
4. 对于序列(字符串, 列表, 元组), 要注意空序列是false. 因此 `if not seq:` 或者 `if seq:` 比 `if len(seq):` 或 `if not len(seq):` 要更好.
5. 处理整数时, 使用隐式false可能会得不偿失(即不小心将None当做0来处理). 你可以将一个已知是整型(且不是len()的返回结果)的值与0比较.
~~~
Yes: if not users:
print 'no users'
if foo == 0:
self.handle_zero()
if i % 10 == 0:
self.handle_multiple_of_ten()
~~~
~~~
No: if len(users) == 0:
print 'no users'
if foo is not None and not foo:
self.handle_zero()
if not i % 10:
self.handle_multiple_of_ten()
~~~
6. 注意‘0’(字符串)会被当做true.
## 过时的语言特性
> Tip
> 尽可能使用字符串方法取代字符串模块. 使用函数调用语法取代apply(). 使用列表推导, for循环取代filter(), map()以及reduce().
定义:
当前版本的Python提供了大家通常更喜欢的替代品.
结论:
我们不使用不支持这些特性的Python版本, 所以没理由不用新的方式.
~~~
Yes: words = foo.split(':')
[x[1] for x in my_list if x[2] == 5]
map(math.sqrt, data) # Ok. No inlined lambda expression.
fn(*args, **kwargs)
~~~
~~~
No: words = string.split(foo, ':')
map(lambda x: x[1], filter(lambda x: x[2] == 5, my_list))
apply(fn, args, kwargs)
~~~
## 词法作用域(Lexical Scoping)
> Tip
> 推荐使用
定义:
嵌套的Python函数可以引用外层函数中定义的变量, 但是不能够对它们赋值. 变量绑定的解析是使用词法作用域, 也就是基于静态的程序文本. 对一个块中的某个名称的任何赋值都会导致Python将对该名称的全部引用当做局部变量, 甚至是赋值前的处理. 如果碰到global声明, 该名称就会被视作全局变量.
一个使用这个特性的例子:
~~~
def get_adder(summand1):
"""Returns a function that adds numbers to a given number."""
def adder(summand2):
return summand1 + summand2
return adder
~~~
(译者注: 这个例子有点诡异, 你应该这样使用这个函数: `sum = get_adder(summand1)(summand2)` )
优点:
通常可以带来更加清晰, 优雅的代码. 尤其会让有经验的Lisp和Scheme(还有Haskell, ML等)程序员感到欣慰.
缺点:
可能导致让人迷惑的bug. 例如下面这个依据 [PEP-0227](http://www.python.org/dev/peps/pep-0227/) 的例子:
~~~
i = 4
def foo(x):
def bar():
print i,
# ...
# A bunch of code here
# ...
for i in x: # Ah, i *is* local to Foo, so this is what Bar sees
print i,
bar()
~~~
因此 `foo([1, 2, 3])` 会打印 `1 2 3 3` , 不是 `1 2 3 4` .
(译者注: x是一个列表, for循环其实是将x中的值依次赋给i.这样对i的赋值就隐式的发生了, 整个foo函数体中的i都会被当做局部变量, 包括bar()中的那个. 这一点与C++之类的静态语言还是有很大差别的.)
结论:
鼓励使用.
## 函数与方法装饰器
> Tip
> 如果好处很显然, 就明智而谨慎的使用装饰器
定义:
[用于函数及方法的装饰器](http://www.python.org/doc/2.4.3/whatsnew/node6.html) (也就是@标记). 最常见的装饰器是@classmethod 和@staticmethod, 用于将常规函数转换成类方法或静态方法. 不过, 装饰器语法也允许用户自定义装饰器. 特别地, 对于某个函数 `my_decorator` , 下面的两段代码是等效的:
~~~
class C(object):
@my_decorator
def method(self):
# method body ...
~~~
~~~
class C(object):
def method(self):
# method body ...
method = my_decorator(method)
~~~
优点:
优雅的在函数上指定一些转换. 该转换可能减少一些重复代码, 保持已有函数不变(enforce invariants), 等.
缺点:
装饰器可以在函数的参数或返回值上执行任何操作, 这可能导致让人惊异的隐藏行为. 而且, 装饰器在导入时执行. 从装饰器代码的失败中恢复更加不可能.
结论:
如果好处很显然, 就明智而谨慎的使用装饰器. 装饰器应该遵守和函数一样的导入和命名规则. 装饰器的python文档应该清晰的说明该函数是一个装饰器. 请为装饰器编写单元测试.
避免装饰器自身对外界的依赖(即不要依赖于文件, socket, 数据库连接等), 因为装饰器运行时这些资源可能不可用(由 `pydoc` 或其它工具导入). 应该保证一个用有效参数调用的装饰器在所有情况下都是成功的.
装饰器是一种特殊形式的”顶级代码”. 参考后面关于 [Main](http://zh-google-styleguide.readthedocs.org/en/latest/google-python-styleguide/python_style_rules/#main) 的话题.
## 线程
> Tip
> 不要依赖内建类型的原子性.
虽然Python的内建类型例如字典看上去拥有原子操作, 但是在某些情形下它们仍然不是原子的(即: 如果__hash__或__eq__被实现为Python方法)且它们的原子性是靠不住的. 你也不能指望原子变量赋值(因为这个反过来依赖字典).
优先使用Queue模块的 `Queue` 数据类型作为线程间的数据通信方式. 另外, 使用threading模块及其锁原语(locking primitives). 了解条件变量的合适使用方式, 这样你就可以使用 `threading.Condition` 来取代低级别的锁了.
## 威力过大的特性
> Tip
> 避免使用这些特性
定义:
Python是一种异常灵活的语言, 它为你提供了很多花哨的特性, 诸如元类(metaclasses), 字节码访问, 任意编译(on-the-fly compilation), 动态继承, 对象父类重定义(object reparenting), 导入黑客(import hacks), 反射, 系统内修改(modification of system internals), 等等.
优点:
强大的语言特性, 能让你的代码更紧凑.
缺点:
使用这些很”酷”的特性十分诱人, 但不是绝对必要. 使用奇技淫巧的代码将更加难以阅读和调试. 开始可能还好(对原作者而言), 但当你回顾代码, 它们可能会比那些稍长一点但是很直接的代码更加难以理解.
结论:
在你的代码中避免这些特性.
背景
最后更新于:2022-04-01 06:06:50
Python 是 Google主要的脚本语言。这本风格指南主要包含的是针对python的编程准则。
为帮助读者能够将代码准确格式化,我们提供了针对 [Vim的配置文件](http://google-styleguide.googlecode.com/svn/trunk/google_python_style.vim) 。对于Emacs用户,保持默认设置即可。