您的位置:首页 > 编程语言 > Python开发

Python核心编程-第六章

2016-05-24 00:00 603 查看
摘要: 序列:字符串、列表和元组

6.1 序列

序列类型有相同访问模式,多个元素可以通过切片来访问:第一个元素的索引是0,第二个是1……最后一个是-1

6.1.1 标准类型操作符

标准类型操作符适用于所有序列类型,见4.5节。意思就是可以进行

对象值的比较,如: ==、<=、>=、!=、<、>等

对象身份比较,如:a is b、a is not b

6.1.2 序列类型操作符

1. 成员关系符(in、not in)

对象 [not] in 序列

如果对象在序列中,则返回True,如果不在就返回False

2. 连接操作符(+)

seq1 + seq2

但是为了更节省内存,如果上述对象是字符串,可以建立一个包含seq1和seq2的列表[seq1,seq2],然后用join方法变为一个对象更划得来。

3. 重复操作符(*)

seq * expr

完成一个序列的多份拷贝。

如果是字符串:

[code=language-python]>>> print 'abc' * 3
'abcabcabc'

如果是列表:

[code=language-python]>>> print [1,2,3] * 3
[1,2,3,1,2,3,1,2,3]

4. 切片操作符( []、[:]、[::] )

seq[index]

index的数值从0到len(seq)-1,这是正索引,还有一种负索引其取值范围从-len(seq)到-1

发现个有意思的的地方:Python是面向对象的,所以seq可以不用赋值给一个变量,能直接用来切片:

[code=plain]>>> print ('a','b','c')[1]
'b'

seq[1:3]表示的是seq的第二个元素(索引为[1])和第三个元素(索引为[2])

5. 用步长索引来进行切片操作

[index1:index2:step]

其中step是步长,也就是两个索引的差值,缺省时是1

如果step是-1的话,就是将序列倒置

6.切片索引的更多内容

有个需要注意的地方是:切片操作的开始结束索引值可以超过字符串长度

比如

[code=plain]>>> (1,2,3,4,5)[-100:100]
[1,2,3,4,5]

但是仅仅是切片,切片,记住是切片!而不是单独一个索引,肯定不会显示列表没有的内容

另外一个重磅问题:

想通过一个循环按照这样的形式显示:每次都把 位于最后的一个字符砍掉。

书上是通过一个None的使用,来巧妙的解决了负索引最小为-1的问题

[code=plain]>>> s = 'abcde'
>>> for i in [None] + range(-1, -len(s), -1):
...     print s[:i]
...
abcde
abcd
abc
ab
a

其中的原理不是特别明白:为什么None可以用来代替索引值?

6.1.3 内建函数(BIF)

1.类型转换

说是类型转换,其实Python不能把一个对象转换为另一个对象,因为它一个对象建立以后就不能更改其身份或者类型了。所谓的转换实际上是浅拷贝了对象的索引,而不是重新建立了一个对象。如果完全拷贝一个对象,需要的是深拷贝。

函数 含义

list(iter) 把可迭代对象转换为列表

str(obj) 把 obj 对象转换成字符串(对象的字符串表示法)

unicode(obj) 把对象转换成 Unicode 字符串(使用默认编码)

basestring() 抽象工厂函数,其作用仅仅是为 str 和 unicode 函数提供父类,所以不能被 实例化,也不能被调用(详见第 6.2 节)

tuple(iter) 把一个可迭代对象转换成一个元组对象

以上是copy来的,感觉作用不大,试了一个list和tuple的函数

[code=plain]>>> list('abd')
['a', 'b', 'd']
>>> list(['a','b'])
['a', 'b']
>>> tuple('abc')
('a','b','c')

iter必须是可迭代对象!

2.可操作

len()、reversed()、sum()只接受序列对象最为参数,而剩下的还可以接受可迭代对象作为参数

函数名 功能

enumerate(iter) 接受一个可迭代对象作为参数,返回一个 enumerate 对象(同时也是一个迭代器),该对象生成由 iter 每个元素的 index 值 和 item 值组成的元组(PEP 279)

len(seq) 返回seq的长度

max(iter,key=None) or max(arg0,arg1...,key=None) 返回iter或(arg0,arg1,...)中的最大值,如果指定了 key, 这个 key 必须是一个可以传给 sort()方法的,用于比较的回 调函数.

min(iter, key=None) or min(arg0, arg1.... key=None) 返回iter里面的最小值;或者返回(arg0,arg2,...)里面的最小值;如果指定了 key,这个 key 必须是一个可以传给 sort()方法的,用于比较的回调函数.

reversed(seq) 接受一个序列作为参数,返回一个以逆序访问的迭代器(PEP 322)

sorted(iter, func=None, key=None, reverse=False) 接受一个可迭代对象作为参数,返回一个有序的列表;可选参数 func,key 和 reverse 的含义跟 list.sort()内建函数的参数含义一 样.

sum(seq, init=0) 返回seq和可选参数init的总和 , 其效果等同于 reduce(operator.add,seq,init)

zip([it0, it1,... itN]) 返回一个列表,其第一个元素是 it0,it1,...这些元素的第 一个元素组成的一个元组,第二个...,类推.

以上为copy的,感觉其实记住len()、max()、reversed()、sorted()、sum()基本就可以了

解释一下zip,接收一个列表,列表每个元素都组成一个元组,所有的元组在作为元素返回到一个列表中

[code=plain]>>> zip(['a', 'b', 'c', 'd', 'e'])
[('a',), ('b',), ('c',), ('d',), ('e',)]


6.2 字符串

Python中没有字符这个类型,这也是字符串用双引号和单引号都一样的原因吧

1. 字符串的创建和赋值

这个之前章节都有说过,不多赘述

[code=plain]>>> aString = 'Hello World!'          # 使用单引号
>>> anotherString = "Python is cool!" # 使用双引号
>>> print aString                     # print 不带引号的 Hello World!
>>> anotherString                     # 不是进行 print 操作,带有引号
'Python is cool!'
>>> s = str(range(4))                 # 把一个列表转换成一个字符串
>>> s
'[0, 1, 2, 3]'

2. 访问字符串的值

其实就是切片和索引,不多说

3. 改变字符串

这个改变更像是‘更新’一样,因为字符串对象创建以后,它就无法改变了,我们改变的只是引用

[code=plain]>>> a = 'hello'
>>> a = a + ' world!'
>>> a
'hello world!'

hello这个对象创建后就无法改变,我们只是将引用a又指向了‘hello world!’

4.删除字符和字符串

通过赋值一个空字符串或者使用del语句来清空:

[code=plain]>>> aString = ''
>>> aString
''
>>> del aString


6.3 字符串和操作符

6.3.1 标准类型操作符

[code=plain]>>> str1 = 'abc'
>>> str2 = 'lmn'
>>> str3 = 'xyz'
>>> str1 < str2
True
>>> str2 != str3
True
>>> str1 < str3 and str2 == 'xyz'
False

字符串是按照ASCII来比较的,比较的首字母

6.3.2 序列操作符切片([]和[:])

和列表元组操作一样,不赘述

1.成员操作符(in,not in)

用来判断一个字符和一个子串(中的字符)是否出现在另一个字符串中

[code=plain]>>> 'bc' in 'abcd'
True
>>> 'n' in 'abcd'
False
>>> 'nm' not in 'abcd'
True

书上6.1例子挺不错的

[code=plain]1#!usr/bin/env python
2
3 import string
4
5 alphas = string.letters + '_'
6 nums = string.digits
7
8 print 'Welcome to the Identifier Checker v1.0'
9 print 'Testees must be at least 2 chars long.'
10 myInput = raw_input('Identifier to test? ')
11
12 if len(myInput) > 1:
13
14     if myInput[0] not in alphas:
15         print '''invalid: first symbol must be
16               alphabetic'''
17     else:
18         for otherChar in myInput[1:]:
19
20             if otherChar not in alphas + nums:
21                 print '''invalid: remaining
22                      symbols must be alphanumeric'''
23                 break
24         else:              #这个缩进和for循环一排,有些不明白
25             print "Okay as an identifier"

核心提示:性能

把重复操作作为参数放到循环里面是非常低效的

[code=plain]while i < len(myString):
print 'character %d is:',myString[i]

小小插曲:格式化字符串为什么在这里少个了 %?困惑……

上述迭代重复计算myString的长度,每次循环都要运行一次len()函数,可以

length = len(myString)

一次性解决问题

2. 连接符(+)

[code=plain]>>> 'Spanish' + 'Inquisition'
'SpanishInquisition'
>>>
>>> 'Spanish' + ' ' + 'Inquisition'
'Spanish Inquisition'
>>>
>>> s = 'Spanish' + ' ' + 'Inquisition' + ' Made Easy'
>>> s
'Spanish Inquisition Made Easy'
>>>
>>> import string
>>> string.upper(s[:3] + s[20]) # archaic (see below)
'SPAM'

就是连接起来,记住这是直接连接,没有空格!!没有空格!!

上述有个import string,这个是为了应用upper()这个方法。这个不是高效的方法,因为Python会为每个参加字符操作的字符串分配地址,包括新生成的。

高效的方法是将所有元素放在一个列表中,用join()方法将其连接起来,或者用格式化操作符(觉得这个方法超nice)

[code=plain]>>> '%s %s' % ('Spanish', 'Inquisition')
'Spanish Inquisition'
>>>
>>> s = ' '.join(('Spanish', 'Inquisition', 'Made Easy'))
>>> s
'Spanish Inquisition Made Easy'
>>>
>>> # no need to import string to use string.upper():
>>> ('%s%s' % (s[:3], s[20])).upper()
'SPAM'

3.编译时字符串连接

[code=plain]>>> foo = "Hello"'world!'       #其实这里双引号单引号没有影响的,而且这里两字符串有无空格没影响
>>> foo
'Helloworld!'

直接写在同一行,没有加号!

用于写网址比较合适

[code=language-python]>>> 'http://' 'localhost' ':8000' '/cgi-bin/friends2.py'
'http://localhost:8000/cgi-bin/friends2.py'

4. 普通字符串转化为Unicode字符串

就是如果普通字符串和Unicode字符串连接,普通会变为Unicode

[code=plain]>>> 'Hello' + u' ' + 'World' + u'!'
u'Hello World!'


6.4 只适用于字符串的操作符

6.4.1 格式化操作符(%)

copy了一下所有的类型(哪里用得到这么多啊,肯定以后在用的过程中在查找的,记住几个典型的就行了)

格式化字符 转换方式

%c 转换成字符(ASCII 码值,或者长度为一的字符串)

%r 优先用 repr()函数进行字符串转换

%s 优先用 str()函数进行字符串转换

%d / %i 转成有符号十进制数

%u 转成无符号十进制数

%o 转成无符号八进制数

%x/%X 转成无符号十六进制数(x/X 代表转换后的十六进制字符的大 小写)

%e/%E 转成科学计数法(e/E 控制输出 e/E) %f/%F 转成浮点数(小数部分自然截断)

%g/%G %e 和%f/%E 和%F 的简写

%% 输出%

还有一些辅助指令,我很怀疑学习Python的人都能记住这些可能用不到的那么多繁复的东西吗?

符号 作用

* 定义宽度或者小数点精度

- 用做左对齐

+ 在正数前面显示加号( + )

在正数前面显示空格

# 在八进制数前面显示零('0'),在十六进制前面显示'0x'或者'0X'(取决于用的是'x'还是'X')

0 显示的数字前面填充‘0’而不是默认的空格

% '%%'输出一个单一的'%'

(var) 映射变量(字典参数)

m.n m是显示的最小总宽度,n 是小数点后的位数(如果可用的话)

Python支持两种格式的输入参数:元组形式和字典形式

元组比较常见:

[code=plain]>>> w, p = 'Web', 'page'
>>> 'http://xxx.yyy.zzz/%s/%s.html' % (w, p)
'http://xxx.yyy.zzz/Web/page.html'

字典见下:

[code=plain]>>> 'There are %(howmany)d %(lang)s Quotation Symbols' % {'lang': 'Python', 'howmany': 3}
'There are 3 Python Quotation Symbols'

简单来讲就是‘键’在前面的语句里面,’‘值’会最终出现在结果当中。值得注意的是‘键’必须在%和类型字母的中间,还要加上小括号

小插曲:怎么看到一些初学者一天写一章的博客,搞的我感觉一周两章的进度实在太慢了!!哎我去,顿时感觉压力好大啊!!!

6.4.2 字符串模板: 更简单的替代品

务必记住一个重要的符号$,是美元符号,因为在新型的Python中可以不用百分号了,我们用美元!!

Template有两个方法,substitute()和safe_substitute()。前者在缺少一键值对的情况下会报错,后者缺少一键值对情况下,会原封不动的把键显示出来

[code=plain]>>> from string import Template
>>> s = Template('There are ${howmany} ${lang} Quotation Symbols')
>>>
>>> print s.substitute(lang='Python', howmany=3) There are 3 Python Quotation
Symbols    #这里是键值对都有的情况
>>>
>>> print s.substitute(lang='Python')     #缺少一对情况下substitute()报错
Traceback (most recent call last):
File "", line 1, in ?
File "/usr/local/lib/python2.4/string.py", line 172, in substitute
return self.pattern.sub(convert, self.template)
File "/usr/local/lib/python2.4/string.py", line 162, in convert val =
mapping[named]
KeyError: 'howmany'
>>>
>>> print s.safe_substitute(lang='Python')   #缺少一对情况下safe_substitute()原封不动写出key
There are ${howmany} Python Quotation
Symbols

6.4.3 原始字符串操作符(r/R)

[code=plain]>>> '\n'
'\n'
>>> print '\n'

>>> r'\n'
'\\n'
>>> print r'\n'
\n

首先我们知道 r' ' 表示 ‘’ 内字符默认不转义,而 \ 需要转义时得用 \\

所以第三种情况就是这么解释,也就是说r‘\n’和‘\\n’是等价的

第四种也可以用print ‘\\n’,显示一样的结果

说实在的,最讨厌这个了

6.4.4 Unicode字符串操作符(u/U)

讲真的,我在Python2.7中发现这个没有什么用

[code=plain]>>> u'abc'
abc

书上说是显示

U+0061 U+0062 U+0063

然而并没有,所以我们还是跳过吧

6.5 内建函数

6.5.1 标准类型函数

cmp(str1,str2)

根据字符串的ASCII码值进行比较,小写字母码值大于大写字母,A码值最小,z码值最大(我是记不住他们码值都多少)

6.5.2 序列类型函数

len()

[code=plain]>>> len('Hello World!')
12

返回的是字符串的字符数,包括空格和!

max() and min()

返回最大或者最小的字符(按照ASCII编码),注意是字符,所以必定有引号

[code=plain]>>> str2 = 'lmn'
>>> str3 = 'xyz'
>>> max(str2)
'n'
>>> min(str3)
'x'

这个尚且能理解,但是混杂了数值的就需要多多记忆了

[code=plain]>>> min('ab12cd')
'1'
>>> min('AB12CD')
'1'

但是我发现这个有个让人困惑的地方,其中的数值12,是1和2,而不是十二,所以比较的最大值也不过9.但是如果要表示十二应该怎么做呢?

enumerate()

接受一个可迭代对象作为参数,返回一个 enumerate 对象(同时也是一个迭代器),该对象生成由 iter 每个元素的 index 值 和 item 值组成的元组

[code=plain]>>> s = 'foobar'
>>> for i, t in enumerate(s):
...     print i, t
...
0 f
1 o
2 o
3 b
4 a
5 r

这里我发现了一个小东西,如果不用for循环来迭代的话,比方我只是

enumerate(s),则会显示

应该是说这个内容存在了地址为0x……这个内存里面,所以for迭代出来是标配

zip()

返回一个列表,其第一个元素是 it0,it1,...这些元素的第 一个元素组成的一个元组,第二个...,类推.(我觉得这个描述就是个渣渣,或者翻译的不行)

还是看例子直观

[code=plain]>>> s, t = 'foa', 'obr'
>>> zip(s, t)
[('f', 'o'), ('o', 'b'), ('a', 'r')]

分别从s,t取出第一个元素拿出来组成一个元组,再取出第二个元素组成第二个元组……

最终把这些元组组成一个列表。多么朴实的语言~

另外我发现,s和t可以字符长度不相同,这会是什么结果呢?

如果s = 'fo'

则zip()作用以后就是[('f','o'),('o'.'b')],也就是说它必定会组成元组对,以最短的那个长度为准

6.5.3 字符串类型函数

raw_input()

不多说

str() and unicode()

前者是输入一个任意的字符串,然后创建该对象的可打印表示。比较拗口,大致就是内容是字符的话就显示字符,内容是数值的话就显示数值形式的字符(不可参加运算)

后者是显示一个对象的Unicode的字符串表示

chr()、unichar和ord()

前者是输入0~255的数值,返回一个对应的字符

中者同前者一样,只是返回一个Unicode的字符

后者是前者的反函数,好吧,我自己编的。输入一个字符,返回其ASCII值或者Unicode值

6.6 字符串内建函数

个人觉得内容好多,也不必非要记着,最常用的记住就ok了。以下为copy~

方法 描述

string.capitalize() 把字符串的第一个字符大写

string.center(width) 返回一个原字符串居中,并使用空格填充至长度 width 的新字符 串

string.count(str, beg=0, end=len(string)) 返回 str 在 string 里面出现的次数,如果 beg 或者 end 指定则 返回指定范围内 str 出现的次数

string.decode(encoding='UTF-8', errors='strict') 以 encoding 指定的编码格式解码 string,如果出错默认报一个 ValueError 的 异 常 , 除 非 errors 指 定 的 是 'ignore' 或 者 'replace'

string.encode(encoding='UTF-8', errors='strict') 以 encoding指定的编码格式编码 string,如果出错默认报一个 ValueError 的异常,除非 errors 指定的是'ignore'或者'replace'

string.endswith(obj, beg=0, end=len(string)) 检查字符串是否以 obj 结束,如果 beg 或者end 指定则检查指 定的范围内是否以 obj 结束, 如果是, 返回 True,否则返回 False.

string.expandtabs(tabsize=8) 把字符串 string 中的 tab 符号转为空格, 默认的空 格数 tabsize 是 8.

string.find(str, beg=0, end=len(string)) 检测 str 是否包含在 string 中,如果 beg 和 end 指定范围, 则检查是否包含在指定范围内,如果是返回开始的索引值,否则 返回-1

string.index(str, beg=0, end=len(string)) 跟 find()方法一样,只不过如果 str 不在 string 中会报一个异常.

string.isalnum() 如果 string 至少有一个字符并且所有字符都是字母或数字则返 回 True,否则返回 False

string.isalpha() 如果 string 至少有一个字符并且所有字符都是字母则返回 True, 否则返回 False

string.isdecimal() 如果 string 只包含十进制数字则返回 True 否则返回 False.

string.isdigit() 如果 string 只包含数字则返回 True 否则返回 False.

string.islower() 如果 string 中包含至少一个区分大小写的字符,并且所有这些(区分 大小写的)字符都是小写,则返回 True,否则返回 False

string.isnumeric() 如果 string 中只包含数字字符,则返回 True,否则返回 False

string.isspace() 如果 string 中只包含空格,则返回 True,否则返回 False.

string.istitle() 如果 string 是标题化的(见 title())则返回 True,否则返回 False string.isupper()b, c 如果 string 中包含至少一个区分大小写的字符, 并且所有这些(区分 大小写的)字符都是大写,则返回 True,否则返回 False

string.join(seq) 以 string 作为分隔符,将 seq 中所有的元素 (的字符串表示)合并为一个新的字符串

string.ljust(width) 返回一个原字符串左对齐,并使用空格填充至长度 width 的新字符串

string.lower() 转换 string 中所有大写字符为小写.

string.lstrip() 截掉 string 左边的空格

string.partition(str) 有点像 find()和 split()的结合体,从 str 出现的第一个位置起, 把字符串string成一个3元素的元组(string_pre_str,str,string_post_str),如果 string 中不包含 str 则 string_pre_str == string.

string.replace(str1, str2, num=string.count(str1))把 string 中的 str1 替换成 str2,如果 num 指定, 则替换不超过 num 次.

string.rfind(str, beg=0,end=len(string)) 类似于 find()函数, 不过是从右边开始查 找.

string.rindex( str, beg=0,end=len(string)) 类似于 index(), 不过是从右边开始.

string.rjust(width) 返回一个原字符串右对齐,并使用空格填充至长度 width 的新字符串

string.rpartition(str) 类似于 partition()函数,不过是从右边开始查找.

string.rstrip() 删除 string 字符串末尾的空格.

string.split(str="", num=string.count(str)) 以 str 为分隔符切片 string,如果 num 有指定值,则仅分隔 num 个子字符串

string.splitlines(num=string.count('\n')) 按照行分隔, 返回一个包含各行作为元素 的列表, 如果 num 指定则仅切片 num 个 行.

string.startswith(obj, beg=0,end=len(string)) 检查字符串是否是以 obj 开头,是则 返回 True,否则返回 False。如果 beg 和 end 指定值,则在指定范围内检查.

string.strip([obj]) 在 string 上执行 lstrip()和 rstrip()

string.swapcase() 翻转 string 中的大小写

string.title() 返回"标题化"的 string,就是说所有单词都是以大写开始,其余 字母均为小写(见 istitle())

string.translate(str, del="") 根据 str 给出的表(包含 256 个字符)转换 string 的字符, 要过滤掉的字符放到 del 参数中

string.upper() 转换 string 中的小写字母为大写

string.zfill(width) 返回长度为 width 的字符串,原字符串 string 右对齐,前面填充 0

来,告诉我,谁能记住这么多函数?突然想起当年学习51单片机,指令集非常多非常多,有加减乘除四种,但是呢速度慢。后来学习了一种430单片机,人家只进行加法计算,通过移位来转换为乘法除法。你说哪种更为先进,对学习者更友好呢?

[code=plain]>>> quest = 'what is your favorite color?'
>>> quest.capitalize()               #字符串首字母大写
'What is your favorite color?'
>>>
>>> quest.center(40)                 #40字符位居中排列
' what is your favorite color? '
>>>
>>> quest.count('or')                #计数其中'or'的数量
2
>>>
>>> quest.endswith('blue')           #是否以'blue'结尾
False
>>>
>>> quest.endswith('color?')         #是否以'color?'结尾
True
>>>
>>> quest.find('or', 30)             #从索引30开始,是否有'or'
-1
>>>
>>> quest.find('or', 22)             #从索引22开始,是否有'or',是的话返回索引
25
>>
>>> quest.index('or', 10)            #同上,否的话报异常
16
>>>
>>> ':'.join(quest.split())          #先用空格将quest拆开,在用':'连接
'what:is:your:favorite:color?'
>>> quest.replace('favorite color', 'quest')#用后者替换前者
>>>
'what is your quest?'
>>>
>>> quest.upper()                    #全部大写化
'WHAT IS YOUR FAVORITE COLOR?'


6.7 字符串的独特特性

6.7.1 特殊字符串和控制字符串

转义字符

/X 八进制 十进制 十六进制 字符 说明

\0 000 0 0x00 NUL 空字符 Nul

\a 007 7 0x07 BEL 响铃字符

\b 010 8 0x08 BS 退格

\t 011 9 0x09 HT 横向制表符

\n 012 10 0x0A LF 换行

\v 013 11 0x0B VT 纵向制表符

\f 014 12 0x0C FF 换页

\r 015 13 0x0D CR 回车

\e 033 27 0x1B ESC 转义

\" 042 34 0x22 " 双引号

\' 047 39 0x27 ' 单引号

\\ 134 92 0x5C \ 反斜杠

其实copy以后还要转换,没那么简单。以上是全部的转义,相信绝大多数都用不到

6.7.2 三引号

有意思的东西

[code=plain]>>> hi = '''hi
there'''
>>> hi       # repr()
'hi\nthere'
>>> print hi # str()
hi there


6.7.3 字符串不变性

字符串创建以后就不能改变了,

[code=plain]>> s = 'abc'
>>>
>>> id(s)
135060856
>>>
>>> s += 'def'
>>> id(s)
135057968

所谓的‘改变’,仅仅是改变其引用,原先的‘abc’被释放掉了

不变性的另一层含义是:无法通过切片来赋值改变

还是上述例子

s[1] = z

这样是会报错的

6.8 Unicode

老实讲,这个真的不好理解。自己整理了一些,看看就好,很多细枝末节其实不用扣得那么紧的

编码:计算机只会处理数字,所以把文本等格式转化为数字的过程叫做编码

解码:将数字转为文本等格式

最早的计算机在设计时采用8个
比特(bit)作为一个字节(byte),所以,一个字节能表示的最大的整数就是255(二进制11111111=十进制25
5),如果要表示更大的整数,就必须用更多的字节。比如两个字节可以表示的最大整数是 65535 ,4个字节可以表
示的最大整数是 4294967295 。
由于计算机是美国人发明的,因此,最早只有127个字母被编码到计算机里,也就是大小写英文字母、数字和一些符
号,这个编码表被称为 ASCII 编码,比如大写字母 A 的编码是 65 ,小写字母 z 的编码是 122 。
但是要处理中文显然一个字节是不够的,至少需要两个字节,而且还不能和ASCII编码冲突,所以,中国制定
了 GB2312 编码,用来把中文编进去。
你可以想得到的是,全世界有上百种语言,日本把日文编到 Shift_JIS 里,韩国把韩文编到 Euc‐kr 里,各国有各国
的标准,就会不可避免地出现冲突,结果就是,在多语言混合的文本中,显示出来会有乱码。

因此,Unicode应运而生。Unicode把所有语言都统一到一套编码里,这样就不会再有乱码问题了。
Unicode标准也在不断发展,但最常用的是用两个字节表示一个字符(如果要用到非常偏僻的字符,就需要4个字
节)。现代操作系统和大多数编程语言都直接支持Unicode。
现在,捋一捋ASCII编码和Unicode编码的区别:ASCII编码是1个字节,而Unicode编码通常是2个字节。
字母 A 用ASCII编码是十进制的 65 ,二进制的 01000001 ;
字符 0 用ASCII编码是十进制的 48 ,二进制的 00110000 ,注意字符 '0' 和整数 0 是不同的;
汉字 中 已经超出了ASCII编码的范围,用Unicode编码是十进制的 20013 ,二进制的 01001110 00101101 。
你可以猜测,如果把ASCII编码的 A 用Unicode编码,只需要在前面补0就可以,因此, A 的Unicode编码
是 00000000 01000001 。
新的问题又出现了:如果统一成Unicode编码,乱码问题从此消失了。但是,如果你写的文本基本上全部是英文的
话,用Unicode编码比ASCII编码需要多一倍的存储空间,在存储和传输上就十分不划算。
所以,本着节约的精神,又出现了把Unicode编码转化为“可变长编码”的 UTF‐8 编码。UTF­8编码把一个Unicode字
符根据不同的数字大小编码成1­6个字节,常用的英文字母被编码成1个字节,汉字通常是3个字节,只有很生僻的字

符才会被编码成4­-6个字节。如果你要传输的文本包含大量英文字符,用UTF­8编码就能节省空间:

字符 ASCII Unicode UTF-­8
A 01000001 00000000 01000001 01000001
中 x 01001110 00101101 11100100 10111000 10101101
从上面的表格还可以发现,UTF­8编码有一个额外的好处,就是ASCII编码实际上可以被看成是UTF­8编码的一部
分,所以,大量只支持ASCII编码的历史遗留软件可以在UTF­8编码下继续工作。
搞清楚了ASCII、Unicode和UTF­8的关系,我们就可以总结一下现在计算机系统通用的字符编码工作方式:
在计算机内存中,统一使用Unicode编码,当需要保存到硬盘或者需要传输的时候,就转换为UTF-­8编码。
用记事本编辑的时候,从文件读取的UTF-­8字符被转换为Unicode字符到内存里,编辑完成后,保存的时候再把Unic
ode转换为UTF­8保存到文件

摘自《廖学峰Python2.7》

6.9 相关模块

模块没有一个见到过的,省略……以后用到再说

6.10 字符串关键点总结

记录些小东西吧

一些引号分隔的字符

可以把字符串看成是 Python 的一种数据类型,在 Python 单引号或者双引号之间的字符数 组或者是连续的字符集合.在 Python 中最常用两个引号是单引号(')和双引号(")。字符串 的实际内容是这些单引号(')或者双引号(")之间的字符,不包括引号本身.

Python 字符串不是通过 NUL 或者'\0'来结束的

C 编程的一个主要问题是你访问了一个字符串后面的本不属于你的空间,这种情况发生在你没有在字符串末尾添加终结符,NUL 或者'\0'(ASCII 值为 0)的时候.Python 不仅为你自动管理内 存,而且也把 C 的这个负担或者说是小麻烦去掉了.Python 中的字符串不是以 NUL 结束的,所以 你不需要为是否已经添加终结符担心.字符串中只包含你所定义的东西,没有别的.

6.11 列表

1.创建列表类型数据并赋值

创建和赋值两件事在一个表达式里面就实现了

[code=plain]>>> aList = [123, 'abc', 4.56, ['inner', 'list'], 7-9j]
>>> print aList
[123, 'abc', 4.56, ['inner', 'list'], (7-9j)]

2.访问列表中的值

通过下标或者切片可实现,需要一再注意的是:索引值

[code=plain]>>> aList[0]
123
>>> aList[1:4]
['abc', 4.56, ['inner', 'list']]
>>> aList[:3]
[123, 'abc', 4.56]
>>> aList[3][1]
'list'

3.更新列表

通过赋值语句或者append()内建函数实现,后续会讲到详细的

这里需要回忆一下,字符串没有办法更新因为字符串是不可变对象,而列表是可变对象

[code=plain]>>> aList[2] = 'float replacer'
>>> aList
[123, 'abc', 'float replacer', ['inner', 'list'], (7-9j)]
>>>
>>> anotherList.append("hi, i'm new here")
>>> print anotherList
[None, 'something to see here', "hi, i'm new here"]

4.删除列表元素或列表本身

后续会有详尽的

[code=plain]>>> aList
[123, 'abc', 'float replacer', ['inner', 'list'], (7-9j)]
>>> del aList[1]      #在知道索引的情况下用del
>>> aList
[123, 'float replacer', ['inner', 'list'], (7-9j)]
>>> aList.remove(123)  #不知道索引,只知道内容用remove
>>> aList
['float replacer', ['inner', 'list'], (7-9j)]
>>> del aList         #直接删除列表本身


6.12 操作符

6.12.1 标准类型操作符

还是=、<、>这些东西。但是列表的比较很诡异

[code=plain]>>> list1 = ['a','b','1']
>>> list2 = ['a','b','2']
>>> list3 = ['b','2','3']
>>> list1 > list2
False
>>> list1 < list3
True

list1和list2比较,它们先比较第一个元素,打平,第二个元素也打平,第三个决出胜负

list1和list3比较,第一个元素就决出胜负。所以列表的比较是直到有一方胜出为止

6.12.2 序列类型操作符

1.切片[][:]

跟字符串操作一样,但是还有一个更加灵活的功能:

简单来说,列表的元素也可以是序列操作的对象。

[code=plain]>>> list1 = [[1,2,3],'a','b']
>>> list1[0][2] = 'c'
>>> list1
[[1,2,'c'],'a','b']

2. 成员关系操作(in,not in)

用于检测一个对象是否在一个列表当中

有个很典型的例子:

[code=plain]>>> list = [['abc',2],(9+4j),'c']
>>> 'c' in list
True
>>> 2 in list
False
>>> 2 in list[0]
True

因为list当中没有元素2,元素2是list[0]当中的

3. 连接操作符(+)

只能在同类型之间连接,任何忽视同类型的操作都会报错!

比方列表只能连接列表,列表不能连接字符串。

另外,这种连接操作是新建一个列表,将两个列表内容放在一起

如果是在某个列表基础上扩展,则是用list.append()来实现

[code=plain]>>> num_list = [43, -1.23, -2, 6.19e5]
>>> str_list = ['jack', 'jumped', 'over', 'candlestick']
>>> mixup_list = [4.0, [1, 'x'], 'beef', -1.9+6j]
>>>
>>> num_list + mixup_list
[43, -1.23, -2, 619000.0, 4.0, [1, 'x'], 'beef', (-1.9+6j)]
>>>
>>> str_list + num_list
['jack', 'jumped', 'over', 'candlestick', 43, -1.23, -2, 619000.0]

比方以下这种就会报错:

[code=plain]>>> num_list + 'new item'
Traceback (innermost last):
File "", line 1, in ?
TypeError: illegal argument type for built-in operation

4. 重复操作符(*)

[code=plain]>>> num = [a,b]
>>> num * 2
[a,b,a,b]


6.12.3 列表类型操作符和列表解析

重要的内容是列表解析:

[code=plain]>>> [ i * 2 for i in [8, -2, 5] ]
[16, -4, 10]
>>> [ i for i in range(8) if i % 2 == 0 ]
[0, 2, 4, 6]

第八章会详细讲到

6.13 内建函数

6.13.1 标准类型函数

cmp()函数

以下是比较方法

1. 对两个列表的元素进行比较.

2. 如果比较的元素是同类型的,则比较其值,返回结果.

3. 如果两个元素不是同一种类型,则检查它们是否是数字.

a. 如果是数字,执行必要的数字强制类型转换,然后比较.

b. 如果有一方的元素是数字,则另一方的元素"大"(数字是"最小的")

c. 否则,通过类型名字的字母顺序进行比较.

4. 如果有一个列表首先到达末尾,则另一个长一点的列表"大".

5. 如果我们用尽了两个列表的元素而且所有元素都是相等的,那么结果就是个平局,就 是说返回一个 0.

6.13.2 序列类型函数

1. len()

返回列表的元素个数

2. max() 和min()

在字符串中,可以用这个两个函数找出字符结果,而在列表当中,返回的是元素(如果列表的元素都是同一种数据类型)

[code=plain]>>> num = [1,2,4+44j,2e+30]
>>> max(num)
Traceback (most recent call last):
File "", line 1, in
TypeError: no ordering relation is defined for complex numbers
>>> num = [2,3,4,5]
>>> max(num)
5
>>> min(num)
2
>>> str = ['a','b','c']
>>> max(str)
'c'
>>> list = num + str
>>> list
[2, 3, 4, 5, 'a', 'b', 'c']
>>> max(list)
'c'
>>> list.append(3+7j)
>>> list
[2, 3, 4, 5, 'a', 'b', 'c', (3+7j)]
>>> max(list)      #最大值还能显示字母,到后面最小值就报错了,可见数字这种数据类型是很小的
'c'
>>> min(list)
Traceback (most recent call last):
File "", line 1, in
TypeError: no ordering relation is defined for complex numbers
>>> list[0] = ['banana','apple']
>>> list
[['banana', 'apple'], 3, 4, 5, 'a', 'b', 'c', (3+7j)]
>>> max(list)         #元素是列表但是不影响最大值是字母,然而最小值还是没有
'c'
>>> min(list)
Traceback (most recent call last):
File "", line 1, in
TypeError: no ordering relation is defined for complex numbers

以上是自己尝试的结果

3. sorted()和reversed()

sorted()是按照字典序排列一个列表(字典序就是ASCII表的顺序)

reversed()是反转一个列表

4. enumerate()和zip()

用法和字符串那里是一样的,还是copy点例子吧

[code=plain]>>> albums = ['tales', 'robot', 'pyramid']
>>> for i, album in enumerate(albums):
... print i, album
...
0 tales
1 robot
2 pyramid
>>>
>>> fn = ['ian', 'stuart', 'david']
>>> ln = ['bairnson', 'elliott', 'paton']
>>>
>>> for i, j in zip(fn, ln):
... print ('%s %s' % (i,j)).title()     #将首字母大写化,这里的首字母是所有单词的首字母
...
Ian Bairnson Stuart Elliott David Paton

5.sum()

[code=plain]>>> a = [6, 4, 5]
>>> reduce(operator.add, a)     #看不懂是什么的话,可以返回看看字符串中sum()函数,有介绍
15
>>> sum(a)
15
>>> sum(a, 5)
20
>>> a = [6., 4., 5.]
>>> sum(a)
15.0

6. list() 和tuple()

前者是将元组转化为列表,后者是将列表转化为元组,即便元组和列表的元素一模一样,两者也不能划等号

[code=plain]>>> aList = ['tao', 93, 99, 'time']
>>> aTuple = tuple(aList)
>>> aList, aTuple
(['tao', 93, 99, 'time'], ('tao', 93, 99, 'time'))
>>> aList == aTuple
False
>>> anotherList = list(aTuple)
>>> aList == anotherList     #将列表变成元组在变成列表,它跟原来的列表是两个对象,仅仅是值相等
True
>>> aList is anotherList     #类型判断是False,因为是两个对象
False
>>> [id(x) for x in aList, aTuple, anotherList]
[10903800, 11794448, 11721544]

6.14 列表类型的内建函数

copy了一下

列表函数 作用

list.append(obj) 向列表中添加一个对象

obj list.count(obj) 返回一个对象 obj 在列表中出现的次数

list.extend(seq) 把序列 seq 的内容添加到列表中

list.index(obj, i=0, j=len(list)) 返回 list[k] == obj 的 k 值,并且 k 的范围在 i<=k

list.insert(index, obj) 在索引量为 index 的位置插入对象

list.pop(index=-1) 删除并返回指定位置的对象,默认是最后一个对象

list.remove(obj) 从列表中删除对象 obj

list.reverse() 原地翻转列表

list.sort(func=None,key=None, 以指定的方式排序列表中的成员,如果 func 和 key 参数指定, 则按照指定的方式比较各个元素,

reverse=False) 如果 reverse 标志被置为 True,则列表以反序排列.

[code=plain]>>> music_media = [45]
>>> music_media
[45]
>>>
>>> music_media.insert(0, 'compact disc')   #在索引为0的地方加入'compact disc'
>>> music_media
['compact disc', 45]
>>>
>>> music_media.append('long playing record')#在列表尾部加入字符串
>>> music_media
['compact disc', 45, 'long playing record']
>>>
>>> music_media.insert(2, '8-track tape')    #在索引2处加入'8-track tape'
>>> music_media
['compact disc', 45, '8-track tape', 'long playing record']

这些还都是比较基础的,如果查找元素在列表中的索引值,可以用in和index()方法实现

[code=plain]>>> 'cassette' in music_media
False
>>> 'compact disc' in music_media
True
>>> music_media.index(45)      #45在列表中的索引值
1
>>> music_media.index('8-track tape')#'8-track tape'在列表中索引值
2
>>> music_media.index('cassette') Traceback (innermost last):
File "", line 0, in ? ValueError: list.index(x): x not in list

最后一步报错,因为列表中本无元素,所以要用in作事先判断

[code=plain]for eachMediaType in (45, '8-track tape', 'cassette'):
if eachMediaType in music_media:
print music_media.index(eachMediaType)

还有sort()和reverse()方法,简单的来说是排序和倒置,注意使用这个方法后没有返回值

[code=plain]>>> music_media
['compact disc', 45, '8-track tape', 'long playing record']
>>> music_media.sort()
>>> music_media
[45, '8-track tape', 'compact disc', 'long playing record']
>>> music_media.reverse()
>>> music_media
['long playing record', 'compact disc', '8-track tape', 45]

核心笔记

关于使用sort()为什么没有返回值。因为可变对象的方法sort()、extend()、reverse()都会在自身原地操作,现有的列表会改变,但是没有返回值!!

而字符串的方法如 'abc'.upper()会有返回值'ABC',因为字符串不可变对象啊,必须返回一个新的对象,要不然我上哪里找它?

自己尝试了一下(sorted()和sort()的区别),如下:

[code=plain]>>> list = ['abc','xyz',2,3]
>>> sorted(list)
[2, 3, 'abc', 'xyz']
>>> list
['abc', 'xyz', 2, 3]
>>> list.sort()
>>> list
[2, 3, 'abc', 'xyz']
>>>

看到没有,如果用sorted()这个函数的话,是有返回值的,但是不会对list造成任何改变。而用其内建函数sort()的话,在自身上面做出改变,不需要返回值

extend()是接收一个列表,将其追加到另一个列表中去,不多讲

6.15 列表的特殊特性

用列表构建其他数据结构

1.堆栈(后进先出LIFO)~~~~想了一下应该是later in first out,好吧,大学的功底看来还有

就相当于叠盘子,最后叠上去的肯定是最上面,用的时候肯定最先拿出来

[code=language-python]#!/usr/bin/env python

stack = []

def pushit():
stack.append(raw_input('Enter new string: ').strip())

def popit():
if len(stack) == 0:
print 'Cannot pop from an empty stack!'
else:
print 'Removed [', `stack.pop()`, ']'

def viewstack():
print stack 	# calls str() internally

CMDs = {'u': pushit, 'o': popit, 'v': viewstack}

def showmenu():
pr = """
p(U)sh
p(O)p
(V)iew
(Q)uit
Enter choice: """
while True:
while True:
try:
choice = raw_input(pr).strip()[0].lower()
except (EOFError,KeyboardInterrupt,IndexError):
choice = 'q'

print '\nYou picked: [%s]' % choice
if choice not in 'uovq':
print 'Invalid option, try again'
else:
break

if choice == 'q':
break
CMDs[choice]()   #感觉这里用的真的是神乎其神啊
print showmenu()
if __name__ == '__main__':
showmenu()

看看就好,可以完美运行

2. 队列(先进先出FIFO),~~~~~first in first out

类似于排队交钱,先去排队的先服务

[code=language-python]#!/usr/bin/env python

queue = []

def enQ():
queue.append(raw_input('Enter new string: ').strip())

def deQ():
if len(queue) == 0:
print 'Cannot pop from an empty queue!'
else:
print 'Removed [', `queue.pop(0)`, ']'   #仅仅在这里做了重大改变,其他地方只是名称的改变而已

def viewQ():
print queue 	# calls str() internally

CMDs = {'e': enQ, 'd': deQ, 'v': viewQ}

def showmenu():
pr = """
(E)nqueue
(D)equeue
(V)iew
(Q)uit
Enter choice: """

while True:
while True:
try:
choice = raw_input(pr).strip()[0].lower()
except (EOFError,KeyboardInterrupt,IndexError):
choice = 'q'

print '\nYou picked: [%s]' % choice
if choice not in 'devq':
print 'Invalid option, try again'
else:
break

if choice == 'q':
break
CMDs[choice]()

if __name__ == '__main__':
showmenu()

亲测有效,多多学习这种编程思路,主函数只有showmenu()

6.16 元组

跟列表非常相似,只是元组用(),列表用[],而且元组是不可变类型对象,通常用作字典的key

1.创建元组并赋值

[code=language-python]>>> aTuple = (123, 'abc', 4.56, ['inner', 'tuple'], 7-9j)
>>> anotherTuple = (None, 'something to see here')
>>> print aTuple
(123, 'abc', 4.56, ['inner', 'tuple'], (7-9j))
>>> print anotherTuple
(None, 'something to see here')
>>> emptiestPossibleTuple = (None,)    #这个才是重点,一定要有逗号
>>> print emptiestPossibleTuple
(None,)
>>> tuple('bar')
('b', 'a', 'r')

2. 访问元组中的值

通过下标和切片,跟列表一样不写了

3.更新元组

同数字跟字符串一样,元组是不可变类型对象,所以说的更新无非是通过现有切片和下标重新构造一个新的元组

4. 移除一个元组的元素及元组本身

无法删除一个元组的元素,可以丢弃那个不用的元素用其他的元素组成一个新的元组

del aTuple 可以删除一个元组

6.17 元组操作符和内建函数

6.17.1 标准类型操作符、序列类型操作符和内建函数

1.创建、重复、连接操作

copy了一下看看就可以了

[code=language-python]>>> t = (['xyz', 123], 23, -103.4)
>>> t
(['xyz', 123], 23, -103.4)
>>> t * 2
(['xyz', 123], 23, -103.4, ['xyz', 123], 23, -103.4)
>>> t = t + ('free', 'easy')
>>> t
(['xyz', 123], 23, -103.4, 'free', 'easy')

2.成员关系操作、切片操作

[code=language-python]>>> 23 in t
True
>>> 123 in t
False
>>> t[0][1]
123
>>> t[1:]
(23, -103.4, 'free', 'easy')

3. 内建函数

[code=language-python]>>> str(t)             #好像没有什么变化
(['xyz', 123], 23, -103.4, 'free', 'easy')
>>> len(t)
5
>>> max(t)
'free'
>>> min(t)
-103.4
>>> cmp(t, (['xyz', 123], 23, -103.4, 'free', 'easy'))
0
>>> list(t)
[['xyz', 123], 23, -103.4, 'free', 'easy']

4. 操作符

[code=language-python]>>> (4, 2) < (3, 5)
False
>>> (2, 4) < (3, -1)
True
>>> (2, 4) == (3, -1)
False
>>> (2, 4) == (2, 4)
True

都跟列表一样,所以索性都copy了,便于以后浏览

6.18 元组的特殊特性

6.18.1 不可变性

数字、字符串和元组字符串是不可变类型

元组的一个好处是,当我们赋值给一个不了解的API时候,可以确保我们的数据不会被更改

6.18.2 元组不是‘不可改变’

体现在一些方面:比方我们把小的元组连接成一个大元组(这是改变吗?),或者重复操作(*)

再有就是改变元组的元素的子元素

[code=language-python]>>> t = (['xyz', 123], 23, -103.4)
>>> t
(['xyz', 123], 23, -103.4)
>>> t[0][1]
123
>>> t[0][1] = ['abc', 'def']
>>> t
(['xyz', ['abc', 'def']], 23, -103.4)

这才像是某种意义上改变了元组

6.18.3 默认集合类型

所有的多对象的,逗号分隔的,没有明确用符号定义的,比如说像用方括号表示列表和用 圆括号表示元组一样,等等这些集合默认的类型都是元组

[code=language-python]>>> 'abc', -4.24e93, 18+6.6j, 'xyz'
('abc', -4.24e+093, (18+6.6j), 'xyz')
>>>
>>> x, y = 1, 2
>>> x, y
(1, 2)

自然而然的变成元组了,很是神奇还有函数返回的多对象,如下:

[code=language-python]def foo1():
:
return obj1, obj2, obj3      #返回是三个对象,默认做一个三元组类型
def foo2():
:
return [obj1, obj2, obj3]    #返回单一一个对象,包含三个对象的列表
def foo3():
:
return (obj1, obj2, obj3)    #显性显示一个元组


6.18.4 单元素元组

第一个元素后面添一个逗 号(,)来表明这是一个元组而不是在做分组操作.

[code=language-python]>>> ('xyz',)
('xyz',)


6.18.5 字典的关键字

元组变量符合可‘hash’标准,而列表变量不行

6.19 相关模块

仅作了解,看了好多都没见过,而且讲的不是特别详细

6.20 拷贝Python对象、浅拷贝和深拷贝

增长见识了!!先上代码吧

[code=language-python]>>> person = ['name', ['savings', 100.00]]
>>> hubby = person[:] # slice copy
>>> wifey = list(person) # fac func copy
>>> [id(x) for x in person, hubby, wifey]
[11826320, 12223552, 11850936]
>>> hubby[0] = 'joe'
>>> wifey[0] = 'jane'
>>> hubby, wifey
(['joe', ['savings', 100.0]], ['jane', ['savings', 100.0]])
>>> hubby[1][1] = 50.00
>>> hubby, wifey
(['joe', ['savings', 50.0]], ['jane', ['savings', 50.0]])  #仅仅是改变了hubby的,为什么wifey的也变了呢

上述原因是进行了一个浅拷贝(包括:完全切片操作[:]、工厂函数,如list()和dict()、copy模块的copy函数),其内容是原来对象元素的引用

注意!!!

亲测了一下,完全切片操作在2.7版本中已经不是浅拷贝了

[code=language-python]>>> list = [1,2,3]
>>> list1 = list
>>> list2 = list[:]
>>> id(list1)
29005224
>>> id(list2)
29006184
>>> id(list)
29005224

为什么名字(上述第一个元素)可以保持不一样呢,因为第一个元素是字符串啊!后边的列表只是拷贝了引用。可以通过查看id来验证

改变前:

[code=language-python]>>> [id(x) for x in hubby]
[9919616, 11826320]
>>> [id(x) for x in wifey]
[9919616, 11826320]

改变后:

[code=language-python]>>> [id(x) for x in hubby]
[12092832, 11826320]
>>> [id(x) for x in wifey]
[12191712, 11826320]          #可以看到列表元素用的是同一个内存,一个改变都会影响其他的引用

这时候就有大招了:深拷贝。这是个函数copy.deepcopy()

[code=language-python]>>> person = ['name', ['savings', 100.00]]
>>> hubby = person
>>> import copy
>>> wifey = copy.deepcopy(person)
>>> [id(x) for x in person, hubby, wifey]
[12242056, 12242056, 12224232]
>>> hubby[0] = 'joe'
>>> wifey[0] = 'jane'
>>> hubby, wifey
(['joe', ['savings', 100.0]], ['jane', ['savings', 100.0]])
>>> hubby[1][1] = 50.00
>>> hubby, wifey
(['joe', ['savings', 50.0]], ['jane', ['savings', 100.0]])

可以查看一下id,看到列表用的不同内存,相互独立了

[code=language-python]>>> [id(x) for x in hubby]
[12191712, 11826280]
>>> [id(x) for x in wifey]
[12114080, 12224792]


6.21 序列类型小结

没必要总结了,发现这本书写的真的是详尽,可以当作宝典来使用~

这一章总结了2天,跟大神一天总结带课后题差距不是一点半点!还要继续加油,才两天就腰酸颈椎疼 了。以后要多加注意!!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息