Python学习笔记
序言
主要是以小甲鱼的视频为主,https://space.bilibili.com/314076440
一些特性
多次调用方法是从左到右.而参数是函数则先执行参数.
一行如果要多个赋值,用;隔开
input().split()
IO
看我放在另一个地方的文档.<D:\Document\md\PYTHON\IO.md>
数据类型
变量
没什么好讲,和C差不多,但是可以用中文做变量名!!!
字符串
普通字符串
用''或者""包裹起来的字符串.转义规则如下:
原始字符串
在字符串前面加个"r"可以改为原始字符串,但如果里面有引号仍然需要转义.不能在输出尾端带有" \ ",系统会认为还没结束.
长字符串
用三个双或者单引号可以实现长字符串(真正意义上的原始字符串)
字符串的组合方式
字符串可以通过加法拼接,可以通过乘法复制.
字符串的修改
python中字符串属于不可修改的数据类型,但是我们可以通过其他方法将它进行变相的修改。
方法一:将字符串转换成列表,修改成功后再利用字符串的join函数将列表转换回字符串。
str1 = "我是字符串数据类型"
- 将字符串类型转换成列表类型
list1 = list(str1)
print(list1)
list1[0] = "它" # 将列表中的第一个数据修改为它
str1 = "".join(list1) # 将列表转换成字符串类型
print(str1)
运行结果:
['我', '是', '字', '符', '串', '数', '据', '类', '型']
它是字符串数据类型
- 通过字符串replace函数将字符串中需要替换的字符进行替换并且重新赋值给字符串。
str1 = "我是字符串数据类型"
str1 = str1.replace("我", "它")
print(str1)
它是字符串数据类型
- 字符串的切片和拼接
str1 = "我是字符串数据类型"
str2 = "它是"
new_str = str1[2:]
str1 = str2 + new_str
print(str1)
它是字符串数据类型
数字类型
整型
python由于是把int型当作字符存入故而可以有无限大的精度,这点也决定了它的速度注定慢.
ps:用整形相除它返回浮点型.
浮点型
python是以IEEE754的标准存储浮点数的,故而同C语言一样会有误差.
科学计数法
1e-5
虚数
可用x+yj或者(x+yj)来表示
对虚数对象是用方式real可查看实部,imag是虚部.
布尔类型
函数bool()可以判断真假
false的对象:None False 0 0.0 0j Decimal(0) Fraction(0,1) '' () [] {} set() range(0)
运算符
Python算术运算符
以下假设变量: a=10,b=20:
运算符 | 描述 | 实例 |
---|---|---|
+ | 加 - 两个对象相加 | a + b 输出结果 30 |
- | 减 - 得到负数或是一个数减去另一个数 | a - b 输出结果 -10 |
* | 乘 - 两个数相乘或是返回一个被重复若干次的字符串 | a * b 输出结果 200 |
/ | 除 - x除以y | b / a 输出结果 2.0 |
% | 取模 - 返回除法的余数 | b % a 输出结果 0 |
** | 幂 - 返回x的y次幂 | a**b 为10的20次方, 输出结果 100000000000000000000 |
// | 取整除 - 返回商的整数部分(向下取整) | >>> 9//2 4 >>> -9//2 -5 |
一些算术函数
- divmod(x,y) 返回x//y和x%y的余数
- abs(x) 取绝对值,还有虚数的模长
- pow(x,y,z) 有个功能,就是加上第三个参数,可以让结果与第三个参数取余
- int() 转化为整数,截断小数
- float 转化为浮点数
- complex("x+yj") 转化为虚数,字符串中间不能有空格
Python比较运算符
以下假设变量a为10,变量b为20:
运算符 | 描述 | 实例 |
---|---|---|
== | 等于 - 比较对象是否相等 | (a == b) 返回 False。 |
!= | 不等于 - 比较两个对象是否不相等 | (a != b) 返回 True。 |
<> | 不等于 - 比较两个对象是否不相等。python3 已废弃。 | (a <> b) 返回 True。这个运算符类似 != 。 |
> | 大于 - 返回x是否大于y | (a > b) 返回 False。 |
< | 小于 - 返回x是否小于y。所有比较运算符返回1表示真,返回0表示假。这分别与特殊的变量 True 和 False 等价。 | (a < b) 返回 True。 |
>= | 大于等于 - 返回x是否大于等于y。 | (a >= b) 返回 False。 |
<= | 小于等于 - 返回x是否小于等于y。 | (a <= b) 返回 True。 |
Python赋值运算符
以下假设变量a为10,变量b为20:
运算符 | 描述 | 实例 |
---|---|---|
= | 简单的赋值运算符(可以令x,y=y,x实现替换) | c = a + b 将 a + b 的运算结果赋值为 c |
+= | 加法赋值运算符 | c += a 等效于 c = c + a |
-= | 减法赋值运算符 | c -= a 等效于 c = c - a |
*= | 乘法赋值运算符 | c *= a 等效于 c = c * a |
/= | 除法赋值运算符 | c /= a 等效于 c = c / a |
%= | 取模赋值运算符 | c %= a 等效于 c = c % a |
**= | 幂赋值运算符 | c **= a 等效于 c = c ** a |
//= | 取整除赋值运算符 | c //= a 等效于 c = c // a |
Python位运算符
按位运算符是把数字看作二进制来进行计算的。Python中的按位运算法则如下:
下表中变量 a 为 60,b 为 13,二进制格式如下:
a = 0011 1100
b = 0000 1101
-----------------
a&b = 0000 1100
a|b = 0011 1101
a^b = 0011 0001
~a = 1100 0011
运算符 | 描述 | 实例 |
---|---|---|
& | 按位与运算符:参与运算的两个值,如果两个相应位都为1,则该位的结果为1,否则为0 | (a & b) 输出结果 12 ,二进制解释: 0000 1100 |
| | 按位或运算符:只要对应的二个二进位有一个为1时,结果位就为1。 | (a| b) 输出结果 61 ,二进制解释: 0011 1101 |
^ | 按位异或运算符:当两对应的二进位相异时,结果为1 | (a ^ b) 输出结果 49 ,二进制解释: 0011 0001 |
~ | 按位取反运算符:将二进制表示中的每一位取反,0 变为 1,1 变为 0。~x 类似于 -x-1 | (~a ) 输出结果 -61 ,二进制解释: 1100 0011 (以补码形式表示),在一个有符号二进制数的补码形式。 |
<< | 左移动运算符:运算数的各二进位全部左移若干位,由<< 右边的数字指定了移动的位数,高位丢弃,低位补0。 | a << 2 输出结果 240 ,二进制解释: 1111 0000 |
>> | 右移动运算符:把">>"左边的运算数的各二进位全部右移若干位,>> 右边的数字指定了移动的位数 | a >> 2 输出结果 15 ,二进制解释: 0000 1111 |
python逻辑运算符
Python语言支持逻辑运算符,以下假设变量 a 为 10, b为 20:
运算符 | 逻辑表达式 | 描述 | 实例 |
---|---|---|---|
and | x and y | 布尔"与" - 如果 x 为 False,x and y 返回 False,否则它返回 y 的计算值。 | (a and b) 返回 20。 |
or | x or y | 布尔"或" - 如果 x 是非 0,它返回 x 的计算值,否则它返回 y 的计算值。 | (a or b) 返回 10。 |
not | not x | 布尔"非" - 如果 x 为 True,返回 False 。如果 x 为 False,它返回 True。 | not(a and b) 返回 False |
注意:返回值不像C语言,而是有可能返回数值本身.它也是短路逻辑.
Python成员运算符
除了以上的一些运算符之外,Python还支持成员运算符,测试实例中包含了一系列的成员,包括字符串,列表或元组。
运算符 | 描述 | 实例 |
---|---|---|
in | 如果在指定的序列中找到值返回 True,否则返回 False。 | x 在 y 序列中 , 如果 x 在 y 序列中返回 True。 |
not in | 如果在指定的序列中没有找到值返回 True,否则返回 False。 | x 不在 y 序列中 , 如果 x 不在 y 序列中返回 True。 |
Python身份运算符
身份运算符用于比较两个对象的存储单元
运算符 | 描述 | 实例 |
---|---|---|
is | is 是判断两个标识符是不是引用自一个对象 | x is y, 类似 id(x) == id(y) , 如果引用的是同一个对象则返回 True,否则返回 False |
is not | is not 是判断两个标识符是不是引用自不同对象 | x is not y , 类似 id(a) != id(b)。如果引用的不是同一个对象则返回结果 True,否则返回 False。 |
注: id() 函数用于获取对象内存地址。
Python运算符优先级
以下表格列出了从最高到最低优先级的所有运算符:
运算符 | 描述 |
---|---|
** | 指数 (最高优先级) |
~ + - | 按位翻转, 一元加号和减号 (最后两个的方法名为 +@ 和 -@) |
* / % // | 乘,除,取模和取整除 |
+ - | 加法减法 |
>> << | 右移,左移运算符 |
& | 位 'AND' |
^| | 位运算符 |
<= < > >= | 比较运算符 |
<> == != | 等于运算符 |
= %= /= //= -= += *= **= | 赋值运算符 |
is is not | 身份运算符 |
in not in | 成员运算符 |
not and or | 逻辑运算符 |
逻辑结构
if-else
if 条件:语句1语句2
elif:语句3
else:语句4
level = ('D'if 0<=score <60 else'C' if 60 <= score <80 else"sdfjkslfjs")
等同于
if 0<=score <60:level='D'
elif 60 <= score < 80:level='C'
else:level="sdfjkslfjs"
// 但是没啥用,因为明显不好看,而且也没省多少力.
循环
while 条件:break //打破循环可使用breakcontinue //跳过本次循环回到循环头
else:语句1 //这里else看起来没什么用,但是如果break跳出,如果条件仍为真,那么就不会执行else.这个就可以检测break有无被启用.
for 变量 in 可迭代对象:
// 这里需要说明一个函数:range(start,stop,step) (start默认为0,stop不包含,step可以为负数,只不过开头和结尾要倒序了)
序列
解包操作:
t = (1,2,3)
x,y,z = t //将t的值全部赋值到xyz上,被称为解包.数量一定要一致.
x,*y = t //如果数量不一样可以这样写,那么x = 1,y = [2,3]
序列增量
列表用*
来拷贝id仍然一样,而元组就不一样了
del
del可以直接删除变量,也可以删除序列特定值,比如x=[1,2,3,4,5] del x[1:4] x >>[1,5] 当然切片也可以 x[1:4] = [] 这里是将这部分切片直接替换了 但是del可以[::2]来删除,切片不能.
迭代器和可迭代对象
迭代器一定是可迭代对象,但迭代器是一次性的. iter可以把可迭代对象变成迭代器,next()可依次把迭代器的元素提取出来,这个类似max()的使用方式
序列通用函数方法
-
list() 转化成列表 tuple() 元组 str()字符串
-
找最大值最小值 max() min() 多个参数和序列都可以实现.如果找不到可以设计一个default 比如
s=[] min(s,default="啥都没有") >>"啥都没有"
-
len() 具体没什么好说的,但是注意这个是用c语言写的,所以有长度限制,应该是long的长度.
-
sum(目标,start) 从start开始加
-
sorted() 与sort类似,但是是新列表
-
reversed() 返回迭代器
-
all() 判断是否都为真,any()是否有一个为真
-
enumerate(x,start) 返回迭代器
x = ["a","b","c","d"] list(enumerate(x,10)) >>[(10,"a"),(11,"b"),(12,"c"),(13,"d")]
-
zip()
x=[1,2,3] y=[4,5,6] z="ilove" list(zip(x,y,z)) >> [(1,4,'i'),(2,5,'l'),(3,6,'o')]
但是如果我们很重视被丢掉的,可以用itertools模块的zip_longest(),那么还会多输出(None,None,'v'),(None,None,'e') -
map() #长度不一样,就以最短的中止
s=[1,2,3,4,5] map(pow,s,[2]*len(s)) >>> [1,4,9,16,25]
-
filter() 类似map()不过是把指定函数运算为真的元素提取出来
列表
列表我觉得就是个可以包含任何类型的数组,或者可以自由变动的结构体.
- 列表也是可迭代对象,或者说序列都可以,比如
for i in rhyme
- 下标同偏移量一样,从0开始(不知道这样说合不合理) 而且有个nb的特性,它可以用负数从末尾开始倒序查找,从-1开始.最关键的是,它是动态的,没有上下界的概念,所以不要用数组的眼光看它.
- 切片:比如有个
a = [1,2,3,4,5,6]
可以用a[0:3]获得[1,2,3],可以用a[:3]获得[1,2,3],a[3:]可获得[4,5,6],[:]就照着上面的原则.当然也有步长.切片可以这样理解,将其去除部分复制导出. - 列表可通过加法相互拼接,也可以通过乘法一直重复,比如[1,2,3]*2=[1,2,3,1,2,3]
- 对对象的方法:
- x.append() 增加一个在末尾
- x.extend([1,2,3]) 增加多个在末尾
- 还可以x[len(x):]=[5,6,7] 这样增加
- x.insert(1,2) 在x的1号位插入2 这个也可以x.insert(len(x),1)
- x.remove(y) 如果有多个y,删除第一个y
- del x[a] 删除下标为a的元素
- x.pop(下标) 字面意思,返回一个元素并删除它
- x.clear() 清空x的所有元素
- x.sort(cmp=None, key=None, reverse=False)
cmp -- 可选参数, 如果指定了该参数会使用该参数的方法进行排序。
key -- 主要是用来进行比较的元素,只有一个参数,具体的函数的参数就是取自于可迭代对象中,指定可迭代对象中的一个元素来进行排序。比如len就会拿他们的长度排序
reverse -- 排序规则,reverse = True 降序, reverse = False 升序(默认)。 - x.reverse() 倒置
- x.count(a) 查找a出现的次数
- x.index(x, start, end) 查找元素a最小的索引值,后面是查找的范围.有找到对象则抛出异常
- x.copy() copy一个x的列表作为返回值
嵌套
列表同数组一样可以嵌套.比如matrix=[[1,2,3],[1,2,3]]或者
matrix=[[1,2,3],
[1,2,3]]
同理也可以[][]来查询
拷贝
可以写成for i in range(3): A[i] = [0]*3
注意不要写成x=[[0]*3] *3
,第二个乘3是重复引用一个对象即[0,0,0],也就是说[[][][0,0,0],[0,0,0],[0,0,0]]的三个[0,0,0]是同一个东西,可以用is判断,这时修改任何一个值都是修改同一个值.标准的创建应该是[none] *3,后面再迭代读入.(可以当成复制了同一个指针?)
一般来说独立创造的列表即使一样,python也不会是同一个对象,因为二者在内存位置不同(字符串则不一样,因为他是常量)
但是如果是这样:x = [1,2,3] y=x x[1]=1 y
会输出[1,1,3]
因为它这里是把x和y与[1,2,3]挂钩,也就是说指针!!!(不确定,感觉像)所以这里要用copy或者y=x[:]
前面两种拷贝方式,都是浅拷贝,即只是拷贝外层,如果出现嵌套就不一样了,拷贝的只是每个的列表的引用值,所以上面那个错误就能解释了,因为*3是直接复制外面的引用值,所以三个都是一样的.
想要实现深拷贝可以用一个模块copy(估计是直接读入)
里面的copy.deepcopy(x)可以实现
列表推导式
[expression(表达式-循环体的内容) for target in iterable]
列表推导式,通过用c语言在解释器解释进而加快了速度,而非步进其语法如下:x[表达式 for 变量 in 列表/范围] (这是创造一个新列表,而不是修改原本列表的值)
举两个例子
matrix = [[1,2,3],[4,5,6],[7,8,9]]
co1 = [row[1] for row in matrix]
co1
>>> [2,5,8] //获得第二列的值
co2 = [matrix[i][i] for i range(len(matrix))]
co2
>>>[1,5,9] //获取对角线的值
进阶
[expression(表达式-循环体的内容) for target in iterable if condition]
先执行for,再执行if,最后执行expression
[expression(表达式-循环体的内容) for target in iterable for target1 in iterable...]
等于
list=[]
matrix = [[1,2,3],[4,5,6],[7,8,9]]
for row in matrix:for col in row:list.append(col)
还可以两者混同,举一反三
比如[[x,y] for j in range(10) if j % 2 == 0 for k in range(10) if k % 3 == 0]
等于
for j in range(10):if j % 2 == 0:for k in range(10):if k % 3 == 0:x.append([x,y])
元组
元组与列表的差别是用()而不是[],同时不支持修改,故而它只用查的功能,既可以用count和index.
元组可以带括号定义,也可以不带x = (1,2,3,4) x = 1,2,3,4
多重赋值也不过是1,2,3,4变成元组,然后解包.
也可以切片,也可以拼接,也可以拷贝,也可以嵌套,也可以用推导式.
生成一个元素的元组要这样写x = (12,)
想要修改也不是没办法,只要将列表存进去就可以修改列表了,进而间接修改了元组.
字符串
先来一个例子
x = "12321"
"是回文" if x = x[::-1] else "不是回文"
这样就可以实现检测了,一快(封装的c语言)二安全.
方法
大小转化
- x.capitalize() 将首字母变成大写,其他全部变为小写,这里是复制出新的而不是修改原本的.
- x.casefold() 返回一个全是小写的新字符串,可以是多个语言
- x.title() 返回一个所有单词首字母是大写其他是小写的新字符串.
- x.swapcase() 大小写反转的新字符串
- x.upper() 返回所有字母都是大写的字符串
- x.lower() 返回所有字母都是小写的字符串,只能是英语
左右对齐
- x.center(width,fillchar=' ') 居中
- x.ljust(width,fillchar=' ') 左对齐(字符串在左边,下同)
- x.rjust(width,fillchar=' ') 右对齐
- zfill(width) 用0填充左边,如果是负数比如说"-520"会被这样填充"-00520"
width是总长度,如果低于字符串长度,则输出原字符串,fillchar是用来填充的字符,默认空格.
查找
- x.count("y",start,end) 从start到end-1的范围查找y出现的次数
- x.find("y"start,end) 从start到end从左到右查找到第一个出现y的下标,如果没有返回-1
- x.rfind("y",start,end) 从start到end从右到左查找到第一个出现y的下标,如果没有返回-1
替换
-
x.expandtabs(y) 将制表符改为y个空格
-
x.replace("x","y",count) 将x替换成y,count是替换次数,默认-1,代表无限.
-
x.translate(table) 用table的规则替换字符串,举例:
table = str.maketrans("ABCDEFG","1234567","love") "I love FishC".translate(table) >>> 'I 6ish3' //相当于映射表,而maketrans的第三个参数代表去掉被替换的字符串中包含的这个
判断
- x.startswitch("y",start,end) x.endswitch("y",start,end) 在指定子字符串中判断y是否在开头或者结尾,返回布尔. 可以运用元组来做,比如说x.startswtich(("你","我","他"))它会在元组找任意一个匹配的元素
- x.istitle() 判断所有单词是否是以大写开头其余都是小写
- x.isupper() 判断所有单词是否是大写写的
- x.isalpha() 判断是否都是字母,注意空格不是
- 判断是否是空字符(含有空格,tab,\n也算)
- x.isprintable 判断是否都可以打印(转义不算)
- isdecimal() isdigit() isnumeric() 范围依次变广
- isalnum() 如果在isalpha isdecimal isnumeric isdigit任意返回true结果都是true
- isidentifier() 判断是否是合法的标识符
- keyword模块的函数iskeyword()可以判断是否是python的保留标识符
截取
-
.lstrip() 将左侧留白去除 .rstrip() 右侧 strip() 左右两边去掉 在空格传入参数可指明删除对象,比如:
"adbce".lstrip("abcd") >>> "e" //因为它是从头或者尾删除的,符合参数中的任意字母则删除,一旦遇到第一个不符合的就停止,也就说它没法删除中间的.
-
.removeprefix(prefix) .removesuffix(suffix) 删除前缀和后缀
截断
-
"www.ilovefishc".partition(".") >>>("www",".","ilovefishc") "www.ilove.fishc".rpartition(".") >>>("www.ilove",".","fishc")
- .split(分隔符(默认空格),分割次数) .rsplit(分隔符(默认空格),分割次数) 返回列表
- 由于各个系统换行不一样所以还可以用这个.splitlines(True/False)默认False 这个是把换行符当作分隔符,而且所有换行符都支持即使同一个文本中是不同的,至于参数表示的是是否保留换行符.
拼接
join() 这个相比+来说快更多次而且是越来越快.
用法是:"分隔符".join([str1,str2,str3] (也可以用其他序列))则可以得出"str1分隔符str2分隔符str3" 想达到拼接的效果只要分隔符是空字符串就行了.
格式化字符串
"1+2={},2的平方{},3的平方{}".format(1+2,2*2,3*3*3)
"{1}看到{0}就很激动!".format("小甲鱼","漂亮小姐姐") //修改输出顺序,也可以引用多次,因为他们被当做元组的元素.
"我叫{name},我爱{fav}".format(name="小甲鱼",fav="python")
同时为了{}可以被输出可以再用{}括起来来注释
"{:.{prec}f}.format(3.1415,prec=2)" >>>"3.14"
还可以用关键参数来写,看来py是直接替换再输出
f"{-520:010}" >> "-000000520" 语法糖形式,用f来表示这是格式化格式,3.6后的特性
位置或者索引:[[fill]align][sign][#][0][width][grouping_option][.precision][type]
-
align
^居中 <左 >右 =放在符号之前数字之后 fill是用来填充的字符 -
sign
+
在正数前输出正号,负数前输出负号 - 只在符号前输出符号-,默认行为 空格在正数前加个空格,负数前加负号. -
0
{:010}.format(-520) >> "-000000520" 只能是数字 -
width
举个例子:{:^10}.format("250") >> " 250 " -
grouping_option
用,
_
来分割每三位数字,比如:"{:,}".format(1234567) >> "1,234,567" -
.precision type
.数字 类型 像C一样,但是如果是f这类浮点数,是显示小数点后多少位,对于g来说则是前后多少位
对于非数字类型是限制最大字段大小,整型不允许使用.- b 二进制
- c unicode字符输出
- d none 十进制
- o 八进制
- x 十六进制
- n 与d类似,有个特性不懂
- e E 科学计数法,e用大小写表示
- g 小数是f形式输出,大数以e的形式输出
- % 百分形式
-
#
参数以二进制八进制十进制十六进制传出会自动加一个#号.
字典
ps:字典在映射关系中吊打一切
字典的基础
e = {"1":"x","2":"y"}
冒号左边为键,右边为值
可以e["3"]="z"
来增加
创建
- 正常创建
- 用dict()但键不能用引号
- 用dict([元组1,元组2,元组3...])元组内部是映射关系
- 把正常创建放在dict()
- 4和2的混合
- 利用3的特性进而用zip函数
增
- dict.fromkeys("Fis",250) >> {'F':250,'i':250,'s':250} 对于初始化特别好用
删(弹出)
- .pop(键,default)
- popitem() 由于3.7后字典变成有序的了,所以可以用这个函数,删除最后一个进入的.
- del 这是真直接删除了,连弹出都没有
- .clear()
改
- update(键或者字典) #字典不能多个,试过了
查
- 最简单的,传入键
- 用a.get(x,default)
- a.setdefault(x,default) #如何用x则返回x,没有则添加键x并对应default,返回default的值
视图对象
如果字典改变,则视图对象也会相应改变,可通过.items() .keys() .values() 获得键值对,键,值三者的视图对象.
在python2中,字典的keys(),values(),items()方法返回的都是列表,但在python3中,他们的返回值不再是列表,而是有各自的类型
拷贝
copy浅拷贝
其他
- len()可获得键值对数量
- in,not in
- list 直接地转化是把key变成列表,要用.values()
- iter() next() #返回key
- reversed() 3.8后可以使用这个倒序
- 字典也有嵌套,也可以
[][][][][]...
- 也可以字典表达式 比如:
{v:k for k,v in d.items()}
替换k v
{x:ord(x) for x in "FishC"}
获得编码值
{x:y for x in [1,3,5] for y in [2,4,6]}
搞新字典,类似于dict(zip(a,b))
集合
集合就和数学的集合一样,有无序性(故而没有下标),唯一性.用set()转化
但可以用in,not in在查看,也可以遍历,但没有顺序.
最大的优势就是利用唯一性查看有无重复s=[1,1,2,3] len(s) == len(set(s)) >> False
方法
- x.isdisjoint(y) 判断有无交集,没有为True
- x.issubset(y) 判断x是否是y的子集
- x.issuperset(y) 判断x是否是y的超集
- x.union(y) 返回x和y的并集
(由于感觉用不到集合,就不细致学习了)
模块
模块使用方式
模块导入:import 模块名 模块名.方法
from 模块名 import 对象名称/* 方法,容易名字冲突,较迟导入会覆盖原本的.
import 模块名 as 关联名称 用关联名称代替模块名.
注意:模块引用后会执行内部所有语句.所以很多开源软件会加上if __name__ == __main__
,因为只有作为脚本实现后__name__
才会变成__main__
,这样就能保证有些语句不会实现,因为他们的名字变成包名了.
包
放在文件夹的一堆模块
import 文件夹名.模块名
3.3之前必须加一个__init__.py
来初始化,本质上这个就是包,其他的是包的模块,所以可以用这个存入包的全局变量,但是要注意,模块无法看到包,故而必须要加上包名才能做到.
总之许多初始化操作可以在这个文件中实现,比如应用内部特定的模块,所以要善于利用.
还有__all__
可以让包特定的模块才能import*
__all__=["模块名1","2"]
也可以遏制包,没法直接访问模块,对于包来说,必须加上这个,才能直接访问模块.
PyPl上传,以后再说.
random 模块方法
随机数 random模块 random.randint(x,y)生成从x到y的随机数(靠系统时间).
可以攻击,需要拿到种子,用random.getstate()可获取随机数的内部状态.
然后用random.setstate()重新设计即可恢复.
random 模块方法如下:
方法 | 描述 |
---|---|
seed() | 初始化随机数生成器 |
getstate() | 返回捕获生成器当前内部状态的对象。 |
setstate() | state 应该是从之前调用 getstate() 获得的,并且 setstate() 将生成器的内部状态恢复到 getstate() 被调用时的状态。 |
getrandbits(k) | 返回具有 k 个随机比特位的非负 Python 整数。 此方法随 MersenneTwister 生成器一起提供,其他一些生成器也可能将其作为 API 的可选部分提供。 在可能的情况下,getrandbits() 会启用 randrange() 来处理任意大的区间。 |
randrange() | 从 range(start, stop, step) 返回一个随机选择的元素。 |
randint(a, b) | 返回随机整数 N 满足 a <= N <= b。 |
choice(seq) | 从非空序列 seq 返回一个随机元素。 如果 seq 为空,则引发 IndexError。 |
choices(population, weights=None, *, cum_weights=None, k=1) | 从 population 中选择替换,返回大小为 k 的元素列表。 如果 population 为空,则引发 IndexError。 |
[shuffle(x, random]) | 将序列 x 随机打乱位置。 |
sample(population, k, *, counts=None) | 返回从总体序列或集合中选择的唯一元素的 k 长度列表。 用于无重复的随机抽样。 |
random() | 返回 [0.0, 1.0) 范围内的下一个随机浮点数。 |
uniform() | 返回一个随机浮点数 N ,当 a <= b 时 a <= N <= b ,当 b < a 时 b <= N <= a 。 |
triangular(low, high, mode) | 返回一个随机浮点数 N ,使得 low <= N <= high 并在这些边界之间使用指定的 mode 。 low 和 high 边界默认为零和一。 mode 参数默认为边界之间的中点,给出对称分布。 |
betavariate(alpha, beta) | Beta 分布。 参数的条件是 alpha > 0 和 beta > 0。 返回值的范围介于 0 和 1 之间。 |
expovariate(lambd) | 指数分布。 lambd 是 1.0 除以所需的平均值,它应该是非零的。 |
gammavariate() | Gamma 分布( 不是伽马函数) 参数的条件是 alpha > 0 和 beta > 0。 |
gauss(mu, sigma) | 正态分布,也称高斯分布。 mu 为平均值,而 sigma 为标准差。 此函数要稍快于下面所定义的 normalvariate() 函数。 |
lognormvariate(mu, sigma) | 对数正态分布。 如果你采用这个分布的自然对数,你将得到一个正态分布,平均值为 mu 和标准差为 sigma 。 mu 可以是任何值,sigma 必须大于零。 |
normalvariate(mu, sigma) | 正态分布。 mu 是平均值,sigma 是标准差。 |
vonmisesvariate(mu, kappa) | 冯·米塞斯分布。 mu 是平均角度,以弧度表示,介于0和 2pi 之间,kappa 是浓度参数,必须大于或等于零。 如果 kappa 等于零,则该分布在 0 到 2pi 的范围内减小到均匀的随机角度。 |
paretovariate(alpha) | 帕累托分布。 alpha 是形状参数。 |
weibullvariate(alpha, beta) | 威布尔分布。 alpha 是比例参数,beta 是形状参数。 |
decimal模块
这是用来高精度计算的模块,具体的我搜不到,我估计我目前也用不到(其实还得学高精度计算)
x=decimal.Decimal('浮点数') //实例化x后就可以用于计算了,注意单引号是用字符串写数字.
函数
空函数:pass
定义函数:def
返回值:return 如果多个值则变成元组输出
参数
- 位置参数:相应位置传参.
- 关键参数:形参 = 实参来传入.
- 默认参数:不写就是,如果要用默认参数,应该放在最前面.
- 收集参数:参数带左侧带星号,类似指针的写法,那么这个参数会收集任意多的参数变为元组.如果两个星号那么就变成字典,这时就必须用关键字参数了,以达到形成键值对的效果.其实就是形参加星打包,实参加星号解包,对,实参也可以
- 默认参数2:可以直接在def中让某个参数等于,如果不修改,那就是默认参数
def func(v,t):pass
func (1,2)
func (v=1,t=2)
func (t,v=1)
def function(*argc):print (argc)
function(1,2,3,4,5)
>>> (1,2,3,4,5)
位置参数要在关键字参数之前
还有不能这样写(以上为例):func(1,v=2)
会报错
然后我们用help函数来查看其他函数时,会有个'/'代表这个的左边参数只能是位置参数.而'*'则代表右边只能是关键字参数.
作用域
同C一样,但是它逆天的在于,只要global 某个全局变量
,他就可以在这个函数内部修改.虽然C可以修改()
嵌套
不多说.不过比较逆天的是,它能在函数内部定义函数.所以它还搞了个nonlocal,让内部函数可以使用外部函数的变量.
LEGB
L:local局部作用域 E:Enclosed嵌套函数的外层函数作用域 G:Global全局作用域 B:Build-In内置作用域,这个B也就说,只要取个与函数名一样的变量名,这个函数用不了,那么我估计函数名的定义就是这个内置作用域
函数如果作为一个返回值或这参数时,不用小括号,但是返回的只是这个函数的引用,加上括号就可以变成调用,比如:
def a():def b():passreturn b
a()()或者f=a() f()==b()
因为对于外层函数来说,不像局部变量调用完后就没有了,他仍然会有保留.
闭包
由于外层函数的特性故而能有以下效果:
def power(exp):def exp_of(base):return base ** expsquare = power(2)
cube = power(3)square(2)
>>>4
cube(5)
>>>125
我们还能用nonlocal来修改,故而可以实现一个带记忆的函数,这样就可以保护这些数据和函数不被随意更改和调用.
装饰器
语法糖:在某一函数上面加上@目标函数
def a(b):def c():b()return c
def d():pass
d = a(d)
# 以上等于以下
def a(b):def c():b()return c
@a
def d():pass
然后装饰器的顺序是从下到上
还有一种
@a(1)
def b:pass
#注意a的写法
def a(msg):def c(b):def d():b()return dreturn c
#我是这样理解的,因为装饰器本质上等于把定义的函数当成参数嘛,那就等于在返回c的时候,b被当作参数放进去了,最后返回了d的引用
lambda
等同于函数 lambda 参数... : 表达式 返回的是一个函数的引用
作用:简单,允许出现在def没法出现的地方
这样就没必要取名
生成器
生成器类似迭代器
用 yield 来代替 return 来实现
每一次运行后结束并记住状态
运用方式可以像这样:
def counter():i=0while i<= 5:yield ii+=1
for i in counter():print(i)
>>1
2
3
4
5
# 也可以用next()
所以没有元组表达式的原因是,把列表表达式换成圆括号,会导致变成生成器表达式
函数档案
在定义函数时在定义行下面打""""""就是说明文档
def a(b):"""你好"""
函数注释
直接上例子:
def times(s:str, n:int) -> str:return s*n
def times(s:list[int], n:int =3)->list:return s*n
def times(s:dict[str,int], n:int =3)->list:return s*n
#这里只是注释,不会强制.
内省
等到类和对象再说.
高阶函数
把函数当参数的函数就是高阶函数.在模块functools中有很多
其中必须要介绍一个reduce函数,reduce(函数,可迭代对象),它可以以此把可迭代对象的成员传入并且每次传入的结果都会记住.
import functools
def add(x,y):return x+y
functools.reduce(add,[1,2,3,4,5]) == add(add(add(add(1,2),3),4),5)
还有个偏函数
import functools
square = functools.partial(pow, exp=2)
square(2)>>4
square(3)>>9
#原理类似闭包
一些需要记住的函数
ord()
type()
异常
try:xxxxx #检测
except (error1,error2,error3) as e:#如果有相应error就执行,一个error就不用元组形式,可以不写直接识别全部,as e是指相应的error描述结果输出到e中pass
except error4:pass
else:pass #这里是没触发异常回调到的地方
finally:无论有没有异常都会触发,必须经过的一环
类和对象
class Turtle:head = 1eyes = 2legs = 4shell = Truedef crawl(self): #self的含义就是对象自己,只有这样python才知道是哪个对象写的,所以必须加selfprint("xxxxxx")x = Turtle() #实例化
y = Turtle()
x.head >> 1
x.crawl() >> xxxxxx
x.a = 1 #可自由增加,绑定到实例化的对象
x.a >> 1
y.a >> error
可使用x.__dict__
内省来检查x的属性,这里会返回字典,可以看得出,属性是通过字典存放的.
注意,如果一个对象的默认值未曾变过,那么如果修改这个值在类中,则这些未变的也会变.
传参时,self好像是默认的所以不用管,可以当成把self去掉
特性
封装
pass
继承
class A:x=520def hello(self):print("你好,我是A~")class B(A): #子类x=820 #可以修改def hello(self): #可以修改print("你好,我是B~")
b = B()
c = 1
isinstance(b,B) >> True #判断是否是该类
isinstance(b,A) >> True #父类也算issubclass(B,A) >> True #判断是否为子类class C(A,B): #从左到右访问,如果左边没有,再去右边找,这种叫做多重继承.pass
c = C()
c.x >> 520
c.hello() >> 你好,我是A~
组合
class dog:def say(self):print("汪汪")class cat:def say(self):print("喵喵")class garden:d=dog()c=cat()def say(self):self.d.say() #确保同一个对象,即绑定self.c.say()g=garden()
g.say()
>>汪汪
>>喵喵
多态
利用继承原理以保证一个同名的方法的不同作用.我感觉更像一种写代码方法,即使建立多个类,只要同名方法,再写一个函数调用,也可以做到多态,
构造函数
__init__
必须用这个
class a:def __init__(self,x,y):print(x,y)
b = a(1,2)
>> 1 2
重写
pass
钻石继承
在面向对象(OOP)编程中,很多情况下会遇到多继承和多重继承的问题和坑,这里带对大家
认识一下其中的一个钻石继承(菱形继承)的问题。
什么时候会出现钻石继承(菱形继承)呢?
当在类树中,由多个类共享同一个父类的时候,钻石继承就出现了。
为什么会出现钻石继承(菱形继承)呢?
这里先带大家理解一下python中关于继承的一些概念1. 多继承:多继承就是在声明类的时候,在类名后面的括号中出现一个以上的父类,这中情况就叫做多继承。也就是一个子类继承一个以上的父类2. 在python中,因为多继承的存在,就会导致出现钻石继承的问题,这里举个例子说明
下面是一个代码的例子:
class Super(object):def __init__(self, name):print('Super类的初始化方法开始执行')self.name = nameprint('Super类的初始化方法执行完毕')class Parent1(Super):def __init__(self, name):print('Parent1类的初始化方法开始执行')Super.__init__(self, name)print('Parent1类的初始化方法执行完毕')class Parent2(Super):def __init__(self, name):print('Parent2类的初始化方法开始执行')Super.__init__(self, name)print('Parent2类的初始化方法执行完毕')class Son(Parent1, Parent2):def __init__(self, name):print('Son类的初始化方法开始执行')Parent1.__init__(self, name)Parent2.__init__(self, name)print('Son类的初始化方法执行完毕')son = Son('spam')
打印结果如下:
Son类的初始化方法开始执行
Parent1类的初始化方法开始执行
Super类的初始化方法开始执行
Super类的初始化方法执行完毕
Parent1类的初始化方法执行完毕
Parent2类的初始化方法开始执行
Super类的初始化方法开始执行
Super类的初始化方法执行完毕
Parent2类的初始化方法执行完毕
Son类的初始化方法执行完毕
在这里我们可以很清楚的看到,Super类的构造函数被调用了两次,这就会出现问题了,如果 在某些应用场景下,Super的构造函数是一个计数器,那么就会导致错误的结果了。
那么怎么解决钻石继承的问题呢?
为了解决这个问题,python中专门引入了MRO顺序来解决这个问题。
MRO顺序本质上执行的是广度优先搜索,从左到右,搜索完同一层级的时候,向上爬升。
保证了每个类中的方法只会被执行一次。避免了同一个类被调用多次的情况。
查看MRO顺序
类名.__mro__(<class '__main__.Son'>, <class '__main__.Parent1'>, <class '__main__.Parent2'>, <class '__main__.Super'>, <class 'object'>)
这里就引出了python中super()函数的作用了
1. super()函数是用来调用父类的一个方法,是为了解决多重继承的问题的
2. 使用super()函数调用的是在mro顺序中的直接父类
3. super()的主要作用是不需要使用父类名来调用父类的方法,单子类改为继承其他父类的时候,不需要对子类内部的调用父类的函数做任何修改就可以调用新父类的方法。增强了代码的可维护性。不需要在所有调用的地方进行修改。
4. super()函数返回一个代理对象作为代表来调用父类的方法。对于访问已经在类中重写的继承方法是十分有用的
然后呢,注意,mro的顺序,它本质上是遍历所有,所以即使没继承(某个没继承任何父类的父类),都有可能直接调用. 详情看小甲鱼的类与对象V
私有变量
就是在类中的属性左边加上__
class x:__y=1
X = x()
X.__y >> error
X._x__y >> 1 #本质上是改名,硬要访问还是能做到,不过都加上了,说明不希望嘛,还是建议用间接访问的方式来解决.然后呢?这个后期添加是没用的.
用单个下划线开头的就是约定俗成的内部变量,结尾的是...没必要了解,除非遇到了,那再搜吧,大概作用就如同class_
,然后你就可以用class做变量名了.
slots
类本质上用字典来存属性,当时如果只有几个固定的话就没意义,因为字典是拿空间换时间.这是我们可以这样写:
class c:__slots__ = ["x","y"] #想要初始的变量名,这样做后,就不可以添加新的变量名了.
这不仅能这样,还能保证不被滥用,相当于锁了.但如果是继承的话,在子类还会有个字典,只有父类的不会按照字典存放.
魔术方法
最开始的是__new__
,这是用来创造一个class类型的初始,一般不用,都是python自己调用.有两种作用,第一种是在元类中构建类,第二种是在继承不可变数据类型时达到能够修改的目的,见下:
class Capstr(str): #任何类型实质上是类def __new__(cls,string): string = string.upper()return super().__new__(cls,string)
cs = Capstr("FishC")
cs
>> 'FISHC'
其实没看太懂这里,以后再说吧.
大概可以理解为拦截,就是这些魔法方法,都是相对应的拦截,就可以在他们之前做任何事
Python 魔术方法是为我们的自定义类添加功能的特殊方法。 它们被双下划线包围(例如__add __()
)。
Python 中有许多魔术方法。 它们中的大多数用于非常特殊的情况。 我们将提到一些更流行的方法。
__add__
方法
__add__()
方法用于实现加法运算。 在 Python 中,数字不是原始文字,而是对象。 num + 4
表达式等效于num.__add__(4)
。
add_dict.py
#!/usr/bin/env pythonclass MyDict(dict):def __add__(self, other):self.update(other)return MyDict(self)a = MyDict({'de': 'Germany'})
b = MyDict({'sk': 'Slovakia'})print(a + b)
在示例中,我们有一个自定义词典,该词典使用__add__()
实现加法运算。
class MyDict(dict):def __add__(self, other):self.update(other)return MyDict(self)
自定义词典继承自内置dict
。 __add__()
方法与update()
方法添加两个字典,并返回新创建的字典。
a = MyDict({'de': 'Germany'})
b = MyDict({'sk': 'Slovakia'})
我们创建两个简单的字典。
print(a + b)
我们添加两个字典。
$ ./add_dict.py
{'de': 'Germany', 'sk': 'Slovakia'}
这是输出。
__init__
和__str__
方法
__init__()
方法用于初始化对象。 此方法用于实现对象的构造函数。 __str__()
提供了对象可读的输出。
init_str.py
#!/usr/bin/env pythonclass Person:def __init__(self, name, occupation):self.name = nameself.occupation = occupationdef __str__(self):return f'{self.name} is a {self.occupation}'p = Person('John Doe', 'gardener')
print(p)
在示例中,我们有一个 Person 类,具有两个属性:name
和occupation
。
def __init__(self, name, occupation):self.name = nameself.occupation = occupation
在__init__()
方法中,我们将实例变量设置为传递给构造函数的值。
def __str__(self):return f'{self.name} is a {self.occupation}'
__str__()
方法可以很好地输出对象。
$ ./init_str.py
John Doe is a gardener
这是输出。
__repr__
方法
__repr__()
方法由内置函数repr()
调用。 当它评估返回对象的表达式时,在 Python shell 上使用它。
__str__()
用于提供对象的人类可读版本,__repr__()
用于提供对象的完整表示。 后者的输出也更适合开发人员。
如果缺少__str__()
实现,则将__repr__()
方法用作后备。
def __repr__(self):return '<{0}.{1} object at {2}>'.format(self.__module__, type(self).__name__, hex(id(self)))
对象的__repr__()
方法的默认实现类似于上面的代码。
repr_ex.py
#!/usr/bin/env pythonclass Person:def __init__(self, name, occupation):self.name = nameself.occupation = occupationdef __str__(self):return f'{self.name} is a {self.occupation}'def __repr__(self):return f'Person{{name: {self.name}, occupation: {self.occupation}}}'p = Person('John Doe', 'gardener')print(p)
print(repr(p))
该示例实现了__str__()
和__repr__()
方法。
$ ./repr_ex.py
John Doe is a gardener
Person{name: John Doe, occupation: gardener}
这是输出。
__len__
和__getitem__
方法
__len__()
方法返回容器的长度。 当我们在对象上使用内置的len()
方法时,将调用该方法。 __getitem__()
方法定义项目访问([])运算符。
french_deck.py
#!/usr/bin/env pythonimport collections
from random import choiceCard = collections.namedtuple('Card', ['suit', 'rank'])class FrenchDeck:ranks = [str(i) for i in range(2, 11)] + list('JQKA')suits = ["heart", "clubs", "spades", "diamond"]def __init__(self):self.total = [Card(suit, rank)for suit in self.suits for rank in self.ranks]def __len__(self):return len(self.total)def __getitem__(self, index):return self.total[index]deck = FrenchDeck()print(deck[0])
print(len(deck))
print(choice(deck))
该方法用于实现法语卡片组。
Card = collections.namedtuple('Card', ['suit', 'rank'])
我们使用一个命名的元组来定义一个Card
类。 namedtuple
是用于创建元组类的工厂功能。 每张卡都有一套西装和一个等级。
def __len__(self):return len(self.total)
__len__()
方法返回卡座(52)中的卡数。
def __getitem__(self, index):return self.total[index]
__getitem__()
实现索引操作。
print(deck[0])
我们得到卡组的第一张牌。 这称为__getitem__()
。
print(len(deck))
这将调用__len__()
方法。
$ ./french_deck.py
Card(suit='heart', rank='2')
52
Card(suit='diamond', rank='A')
这是输出。
__int__
和__index__
方法
调用__int__()
方法以实现内置的int()
功能。 当在切片表达式中使用对象以及内置的hex()
,oct()
和bin()
函数时,__index__()
方法将类型转换为 int。
char_ex.py
#!/usr/bin/env pythonclass Char:def __init__(self, val):self.val = valdef __int__(self):return ord(self.val)def __index__(self):return ord(self.val)c1 = Char('a')print(int(c1))
print(hex(c1))
print(bin(c1))
print(oct(c1))
在示例中,我们创建一个自定义的Char
类,该类实现了int()
,hex()
,bin()
和oct()
函数。
./char_ex.py
97
0x61
0b1100001
0o141
这是输出。
__eq __
,__ lt__
和__gt__
方法
__eq__()
实现了==
运算符。 __lt__()
实现了<
运算符,__gt__()
实现了>
运算符。
pouch.py
#!/usr/bin/env pythonimport collectionsCoin = collections.namedtuple('coin', ['rank'])# a gold coin equals to two silver and six bronze coinsclass Pouch:def __init__(self):self.bag = []def add(self, coin):self.bag.append(coin)def __eq__(self, other):val1, val2 = self.__evaluate(other)if val1 == val2:return Trueelse:return Falsedef __lt__(self, other):val1, val2 = self.__evaluate(other)if val1 < val2:return Trueelse:return Falsedef __gt__(self, other):val1, val2 = self.__evaluate(other)if val1 > val2:return Trueelse:return Falsedef __str__(self):return str(self.bag)def __evaluate(self, other):val1 = 0val2 = 0for coin in self.bag:if coin.rank == 'g':val1 += 6if coin.rank == 's':val1 += 3if coin.rank == 'b':val1 += 1for coin in other.bag:if coin.rank == 'g':val2 += 6if coin.rank == 's':val2 += 3if coin.rank == 'b':val2 += 1return val1, val2pouch1 = Pouch()pouch1.add(Coin('g'))
pouch1.add(Coin('g'))
pouch1.add(Coin('s'))pouch2 = Pouch()pouch2.add(Coin('g'))
pouch2.add(Coin('s'))
pouch2.add(Coin('s'))
pouch2.add(Coin('b'))
pouch2.add(Coin('b'))
pouch2.add(Coin('b'))print(pouch1)
print(pouch2)if pouch1 == pouch2:print('Pouches have equal value')elif pouch1 > pouch2:print('Pouch 1 is more valueable than Pouch 2')
else:print('Pouch 2 is more valueable than Pouch 1')
我们有一个可以容纳金,银和青铜硬币的小袋。 一枚金币等于两个银币和六个铜币。 在示例中,我们使用 Python magic 方法为 pouch 对象实现了三个比较运算符。
def __eq__(self, other):val1, val2 = self.__evaluate(other)if val1 == val2:return Trueelse:return False
在__eq__()
方法中,我们首先评估两个小袋的值。 然后我们比较它们并返回布尔结果。
def __evaluate(self, other):val1 = 0val2 = 0for coin in self.bag:if coin.rank == 'g':val1 += 6if coin.rank == 's':val1 += 3if coin.rank == 'b':val1 += 1for coin in other.bag:if coin.rank == 'g':val2 += 6if coin.rank == 's':val2 += 3if coin.rank == 'b':val2 += 1return val1, val2
__evaluate()
方法计算两个袋的值。 它穿过小袋的硬币,并根据硬币的等级增加一个值。
pouch1 = Pouch()pouch1.add(Coin('g'))
pouch1.add(Coin('g'))
pouch1.add(Coin('s'))
Python
Copy
我们创建第一个袋,并在其中添加三个硬币。
if pouch1 == pouch2:print('Pouches have equal value')elif pouch1 > pouch2:print('Pouch 1 is more valueable than Pouch 2')
else:print('Pouch 2 is more valueable than Pouch 1')
我们将小袋与比较运算符进行比较。
2D 向量示例
在下面的示例中,我们介绍了几种其他魔术方法,包括__sub__()
,__mul__()
和__abs__()
。
vector.py
#!/usr/bin/env pythonimport mathclass Vec2D:def __init__(self, x, y):self.x = xself.y = ydef __add__(self, other):return Vec2D(self.x + other.x, self.y + other.y)def __sub__(self, other):return Vec2D(self.x - other.x, self.y - other.y)def __mul__(self, other):return self.x * other.x + self.y * other.ydef __abs__(self):return math.sqrt(self.x ** 2 + self.y ** 2)def __eq__(self, other):return self.x == other.x and self.y == other.ydef __str__(self):return f'({self.x}, {self.y})'def __ne__(self, other):return not self.__eq__(other) u = Vec2D(0, 1)
v = Vec2D(2, 3)
w = Vec2D(-1, 1)a = u + v
print(a)print(a == w)a = u - v
print(a)a = u * vprint(a)
print(abs(u))
print(u == v)
print(u != v)
在示例中,我们有一个Vec2D
类。 我们可以比较,加,减和乘向量。 我们还可以计算向量的长度。
$ ./vector.py
(2, 4)
False
(-2, -2)
3
1.0
False
True
这是输出。
奇技淫巧
函数重生
class E:def __init__(self,name,func):self.name = nameself.func = funcdef __del__(self):self.func(self)def ouuter():x=0def inner(y=None):nonlocal xif y:x=yelse:return xreturn inner
f = outter()
e = E("x",f)
del e
e >> error
g = f()
g >> 引用值
g.name >> 'x'