如果你有一定的编程语言基础,学习 Python 也是不错的选择,因为 Python 很可能就是未来开发的主流方向,多学一门语言,多一个防身技能。而且 Python 有强大的功能库,能非常快速的开发工具,为你的本职开发工作提供护航。
如无特殊情况, 文件头部必须加入#-*-coding:utf-8-*-
标识,即文件一律使用UTF-8编码
统一使用4个空格
class A:
def __init__(self):
pass
def hello(self):
pass
def main():
pass
from myclass import MyClass
import bar
import foo.bar
bar.Bar()
foo.bar.Bar()
[=,-,+=,==,>,in,is not, and]
,
之后要有空格# 正确的写法
def complex(real, imag=0.0):
pass
# 不推荐的写法
def complex(real, imag = 0.0):
pass
简单说,自然语言使用双引号,机器标示使用单引号,因此 代码里 多数应该使用 单引号
"..."
'...'
例如 dict 里的 keyr"..."
"""......"""
docstring 的规范中最其本的两点:
"""Return a foobar
Optional plotz says to frobnicate the bizbaz first.
"""
"""Oneline docstring"""
“#”号后空一格,段落件用空行分开(同样需要“#”号)
至少使用两个空格和语句分开,注意不要使用无意义的注释
作为文档的Docstring一般出现在模块头部、函数和类的头部,这样在python中可以通过对象的__doc__对象获取文档. 编辑器和IDE也可以根据Docstring给出自动提示.
# -*- coding: utf-8 -*-
"""Example docstrings.
This module demonstrates documentation as specified by the `Google Python
Style Guide`_. Docstrings may extend over multiple lines. Sections are created
with a section header and a colon followed by a block of indented text.
Example:
Examples can be given using either the ``Example`` or ``Examples``
sections. Sections support any reStructuredText formatting, including
literal blocks::
"""
# 不推荐的写法(不要写函数原型等废话)
def function(a, b):
"""function(a, b) -> list"""
... ...
# 正确的写法
def function(a, b):
"""计算并返回a到b范围内数据的平均值"""
... ...
模块尽量使用小写命名,首字母保持小写,尽量不要用下划线(除非多个单词,且数量不多的情况)
MAX_CLIENT = 100
常量使用以下划线分隔的大写命名
Python 程序是大小写敏感的,如果写错了大小写,程序会报错。
字符串英文 string ,是 python 中随处可见的数据类型,字符串的识别也非常的简单,就是用「引号」括起来的。
引号包括单引号 ' '
,双引号 " "
和 三引号 ''' '''
,比如 'abc'
,"123"
等等。
注意:三引号 ''' '''
是直接可以分行的。
运行结果:
只有一种整数类型 int,表示为长整型,没有 python2 中的 Long。
包括正整数、负整数和零,是没有小数点的数字。
浮点数跟整数有很多类似的地方,但是浮点数是最折磨人的,也是最难让人捉摸透的。
为什么这么说呢?
看下面的例子 ,像整数一样,只是基本的浮点数加法运算。
可是运算结果,对于初学者来说,可能会接受不了。
这是因为计算机对浮点数的表达本身是不精确的。保存在计算机中的是二进制数,二进制对有些数字不能准确表达,只能非常接近这个数。
所以我们在对浮点数做运算和比较大小的时候要小心。
一个布尔值只有 True
、 False
两种值
布尔值可以用 and
、or
和 not
运算。
and
运算是与运算,只有所有都为 True,and 运算结果才是 True。or
运算是或运算,只要其中有一个为 True,or 运算结果就是 True。not
运算是非运算,它是一个单目运算符,把 True 变成 False,False 变成 True。基本上每种编程语言都有自己的特殊值——空值,在 Python 中,用 None 来表示
方法 | 说明 |
---|---|
int(x [,base ]) | 将x转换为一个整数 |
float(x ) | 将x转换到一个浮点数 |
complex(real [,imag ]) | 创建一个复数 |
str(x ) | 将对象 x 转换为字符串 |
repr(x ) | 将对象 x 转换为表达式字符串 |
eval(str ) | 用来计算在字符串中的有效 Python 表达式,并返回一个对象 |
tuple(s ) | 将序列 s 转换为一个元组 |
list(s ) | 将序列 s 转换为一个列表 |
chr(x ) | 将一个整数转换为一个字符 |
unichr(x ) | 将一个整数转换为 Unicode 字符 |
ord(x ) | 将一个字符转换为它的整数值 |
hex(x ) | 将一个整数转换为一个十六进制字符串 |
oct(x ) | 将一个整数转换为一个八进制字符串 |
变量名必须是大小写英文、数字和下划线(_)的组合,且不能用数字开头
多个变量赋值:
a = b = c = 1 #创建一个整型对象,值为 1,三个变量被分配到相同的内存空间上。
也可以:
a, b, c = 1, 2, "liangdianshui"
List是[]包起来的;
Tuple是()包起来的;
Dict是{}包起来的;
Set是{}包起来的;
List (列表)是 Python 内置的一种数据类型。 它是一种有序的集合,可以随时添加和删除其中的元素。
list1=['值1','value1',123]
name = ['一点水', '两点水', '三点水', '四点水', '五点水']
# 通过索引来访问列表
print(name[2])
# 通过方括号的形式来截取列表中的数据
print(name[0:2]) #从第 0 个开始取,取到第 2 个,但是不包含第 2 个。
所以,也可以:
输出结果:
name = ['一点水', '两点水', '三点水', '四点水', '五点水']
# 通过索引对列表的数据项进行修改或更新
name[1]='2点水'
print(name)
# 使用 append() 方法来添加列表项
name.append('六点水')
print(name)
name = ['一点水', '两点水', '三点水', '四点水', '五点水']
print(name)
# 使用 del 语句来删除列表的的元素
del name[3]
print(name)
Python表达式 | 结果 | 描述 |
---|---|---|
len([1, 2, 3]) | 3 | 计算元素个数 |
[1, 2, 3] + [4, 5, 6] | [1, 2, 3, 4, 5, 6] | 组合 |
['Hi!'] * 4 | ['Hi!', 'Hi!', 'Hi!', 'Hi!'] | 复制 |
3 in [1, 2, 3] | True | 元素是否存在于列表中 |
for x in [1, 2, 3]: print x, | 1 2 3 | 迭代 |
函数&方法 | 描述 |
---|---|
len(list) | 列表元素个数 |
max(list) | 返回列表元素最大值 |
min(list) | 返回列表元素最小值 |
list(seq) | 将元组转换为列表 |
list.append(obj) | 在列表末尾添加新的对象 |
list.count(obj) | 统计某个元素在列表中出现的次数 |
list.extend(seq) | 在列表末尾一次性追加另一个序列中的多个值(用新列表扩展原来的列表) |
list.index(obj) | 从列表中找出某个值第一个匹配项的索引位置 |
list.insert(index, obj) | 将对象插入列表 |
list.pop(obj=list[-1]) | 移除列表中的一个元素(默认最后一个元素),并且返回该元素的值 |
list.remove(obj) | 移除列表中的一个元素(参数是列表中元素),并且不返回任何值 |
list.reverse() | 反向列表中元素 |
list.sort([func]) | 对原列表进行排序 |
tuple 和 List 非常类似,但是 tuple 一旦初始化就不能修改。
元组(tuple) 不可变是指当你创建了 tuple 时候,它就不能改变了,也就是说它也没有 append(),insert() 这样的方法,但它也有获取某个索引值的方法,但是不能赋值。
为什么要有 Tuple 呢?
那是因为 Tuple是不可变的,所以代码更安全。所以建议能用 Tuple代替 list 就尽量用 Tuple。
#使用括号+逗号隔开
tuple1=('两点水','twowter','liangdianshui',123,456)
tuple2='两点水','twowter','liangdianshui',123,456
#创建空元组
tuple3=()
#元组只包含一个元素时
tuple4=(123,)
#如果不加逗号,创建出来的就不是 元组(tuple),而是指 123 这个数了。
#所以如果只有一个元素时,你不加逗号,计算机就根本没法识别你是要进行整数或者小数运算还是表示元组。
tuple1=('两点水','twowter','liangdianshui',123,456)
print(tuple1[1])
元组中的元素值是不允许修改的,但我们可以对元组进行连接组合,还有通过修改其他列表的值从而影响 Tuple的值。
list1=[123,456]
tuple1=('两点水','twowater','liangdianshui',list1)
print(tuple1)
list1[0]=789
list1[1]=100
print(tuple1)
运行结果:
('两点水', 'twowater', 'liangdianshui', [123, 456]) ('两点水', 'twowater', 'liangdianshui', [789, 100])
与字符串一样,元组之间可以使用 +
号和 *
号进行运算。这就意味着他们可以组合和复制,运算后会生成一个新的元组。
Python表达式 | 结果 | 描述 |
---|---|---|
len((1, 2, 3)) | 3 | 计算元素个数 |
(1, 2, 3) + (4, 5, 6) | (1, 2, 3, 4, 5, 6) | 连接 |
('Hi!',) * 4 | ('Hi!', 'Hi!', 'Hi!', 'Hi!') | 复制 |
3 in (1, 2, 3) | True | 元素是否存在 |
for x in (1, 2, 3): print(x) | 1 2 3 | 迭代 |
方法 | 描述 |
---|---|
len(tuple) | 计算元组元素个数 |
max(tuple) | 返回元组中元素最大值 |
min(tuple) | 返回元组中元素最小值 |
tuple(seq) | 将列表转换为元组 |
dict 全称 dictionary,如果学过 Java ,字典就相当于 Java 中的 map,使用键-值(key-value)存储,具有极快的查找速度。
和 list 比较,dict 有以下几个特点:
查找和插入的速度极快,不会随着key的增加而变慢
需要占用大量的内存,内存浪费多
而list相反:
查找和插入的时间随着元素的增加而增加
占用空间小,浪费内存很少
字典是另一种可变容器模型,且可存储任意类型对象。
字典的每个键值(key=>value)对用冒号(:)分割,每个对之间用逗号(,)分割,整个字典包括在花括号({})中 ,格式如下所示:
dict = {key1 : value1, key2 : value2 }
#键必须是唯一的,但值则不必。值可以取任何数据类型,但键必须是不可变的。
#比如:
dict1={'liangdianshui':'111111' ,'twowater':'222222' ,'两点水':'333333'}
dict2={'abc':1234,1234:'abc'}
name = {'一点水': '131456780001', '两点水': '131456780002', '三点水': '131456780003', '四点水': '131456780004', '五点水': '131456780005'}
print(name['两点水'])
注意:如果字典中没有这个键,是会报错的。
dict1={'liangdianshui':'111111' ,'twowater':'222222' ,'两点水':'333333'}
print(dict1)
# 新增一个键值对
dict1['jack']='444444'
print(dict1)
# 修改键值对
dict1['liangdianshui']='555555'
print(dict1)
dict1={'liangdianshui':'111111' ,'twowater':'222222' ,'两点水':'333333'}
print(dict1)
# 通过 key 值,删除对应的元素
del dict1['twowater']
print(dict1)
# 删除字典中的所有元素
dict1.clear()
print(dict1)
# 删除字典
del dict1
方法和函数 | 描述 |
---|---|
len(dict) | 计算字典元素个数 |
str(dict) | 输出字典可打印的字符串表示 |
type(variable) | 返回输入的变量类型,如果变量是字典就返回字典类型 |
dict.clear() | 删除字典内所有元素 |
dict.copy() | 返回一个字典的浅复制 |
dict.values() | 以列表返回字典中的所有值 |
popitem() | 随机返回并删除字典中的一对键和值 |
dict.items() | 以列表返回可遍历的(键, 值) 元组数组 |
python 的 set 和其他语言类似, 是一个无序不重复元素集, 基本功能包括关系测试和消除重复元素。set 和 dict 类似,但是 set 不存储 value 值的。
set1=set([123,456,789])
print(set1)
在 dict (字典) 中创建时,有重复的 key ,会被后面的 key-value 值覆盖的,而 重复元素在 set 中自动被过滤的。
通过 add(key) 方法可以添加元素到 set 中,可以重复添加,但不会有效果。
通过 remove(key) 方法可以删除 set 中的元素
set1=set('hello')
set2=set(['p','y','y','h','o','n'])
#交集
set3=set1 & set2
#并集
set4=set1 | set2
#差集
set5=set1 - set2
Python 程序语言指定任何非 0 和非空(null)值为 True,0 或者 null 为 False。
注意:在条件判断代码中的冒号 :
后、下一行内容是一定要缩进的。不缩进是会报错的。
#单条件
if 判断条件1:
执行语句1……
elif 判断条件2:
执行语句2……
elif 判断条件3:
执行语句3……
else:
执行语句4……
#多条件
if 条件1 and 条件2:
elif 条件3 or 条件4
在 Python 提供了 for 循环和 while 循环。
循环控制语句 | 描述 |
---|---|
break | 在语句块中执行过程中终止循环,并且跳出整个循环 |
continue | 在语句块执行过程中终止当前循环,执行下一次循环 |
pass | pass是空语句,是为了保持程序结构的完整性 |
for iterating_var in sequence:
statements(s)
def 函数名(参数1,参数2....参数n):
函数体
return 语句
# -*- coding: UTF-8 -*-
def print_user_info( name , age , sex = '男' ):
# 打印用户信息
print('昵称:{}'.format(name) , end = ' ')
print('年龄:{}'.format(age) , end = ' ')
print('性别:{}'.format(sex))
return;
# 调用 print_user_info 函数
print_user_info( name = '两点水' ,age = 18 , sex = '女')
print_user_info( name = '两点水' ,sex = '女', age = 18 )
Python 提供了一种元组的方式来接受没有直接定义的参数。这种方式在参数前边加星号 *
。
例如:
# -*- coding: UTF-8 -*-
def print_user_info( name , age , sex = '男' , * hobby):
# 打印用户信息
print('昵称:{}'.format(name) , end = ' ')
print('年龄:{}'.format(age) , end = ' ')
print('性别:{}'.format(sex) ,end = ' ' )
print('爱好:{}'.format(hobby))
return;
# 调用 print_user_info 函数
print_user_info( '两点水' ,18 , '女', '打篮球','打羽毛球','跑步')
运行结果:
昵称:两点水 年龄:18 性别:女 爱好:('打篮球', '打羽毛球', '跑步')
# -*- coding: UTF-8 -*-
def print_user_info( name , age , sex = '男' , ** hobby ):
# 打印用户信息
print('昵称:{}'.format(name) , end = ' ')
print('年龄:{}'.format(age) , end = ' ')
print('性别:{}'.format(sex) ,end = ' ' )
print('爱好:{}'.format(hobby))
return;
# 调用 print_user_info 函数
print_user_info( name = '两点水' , age = 18 , sex = '女', hobby = ('打篮球','打羽毛球','跑步'))
运行结果:
昵称:两点水 年龄:18 性别:女 爱好:{'hobby': ('打篮球', '打羽毛球', '跑步')}
# -*- coding: UTF-8 -*-
def print_user_info( name , *, age , sex = '男' ):
# 打印用户信息
print('昵称:{}'.format(name) , end = ' ')
print('年龄:{}'.format(age) , end = ' ')
print('性别:{}'.format(sex))
return;
# 调用 print_user_info 函数
print_user_info( name = '两点水' ,age = 18 , sex = '女' )
# 这种写法会报错,因为 age ,sex 这两个参数强制使用关键字参数
#print_user_info( '两点水' , 18 , '女' )
print_user_info('两点水',age='22',sex='男')
python 使用 lambda 来创建匿名函数,也就是不再使用 def 语句这样标准的形式定义一个函数。
匿名函数主要有以下特点:
语法:
lambda [arg1 [,arg2,.....argn]]:expression
示例:
# -*- coding: UTF-8 -*-
sum = lambda num1 , num2 : num1 + num2;
print( sum( 1 , 2 ) )
注意:尽管 lambda 表达式允许你定义简单函数,但是它的使用是有限制的。 你只能指定单个表达式,它的值就是最后的返回值。也就是说不能包含其他的语言特性了, 包括多个语句、条件表达式、迭代以及异常处理等等。
特别注意:
# -*- coding: UTF-8 -*-
num2 = 100
sum1 = lambda num1 : num1 + num2 ;
num2 = 10000
sum2 = lambda num1 : num1 + num2 ;
print( sum1( 1 ) )
print( sum2( 1 ) )
运行结果:
10001
10001
这主要在于 lambda 表达式中的 num2 是一个自由变量,在运行时绑定值,而不是定义时就绑定,这跟函数的默认值参数定义是不同的。所以建议还是遇到这种情况还是使用第一种解法。
迭代:
for本身就是一种迭代
# 1、for 循环迭代字符串
for char in 'liangdianshui' :
print ( char , end = ' ' )
# 如果 list 里面一个元素有两个变量,也是很容易迭代的
for x , y in [ (1,'a') , (2,'b') , (3,'c') ] :
print ( x , y )
输出结果:
l i a n g d i a n s h u i
1 a
2 b
3 c
迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。
迭代器只能往前不会后退。
迭代器有两个基本的方法:iter() 和 next(),且字符串,列表或元组对象都可用于创建迭代器,迭代器对象可以使用常规 for 语句进行遍历,也可以使用 next() 函数来遍历。
# 3、tuple(元祖) 对象创建迭代器
tuple1 = ( 1,2,3,4 )
iter3 = iter ( tuple1 )
# for 循环遍历迭代器对象
for x in iter1 :
print ( x , end = ' ' )
print('\n------------------------')
# next() 函数遍历迭代器
while True :
try :
print ( next ( iter3 ) )
except StopIteration :
break
运行结果:
1
2
3
4
通过上面的学习,可以知道列表生成式,我们可以直接创建一个列表。
但是,受到内存限制,列表容量肯定是有限的。而且,创建一个包含 1000 万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。
所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?
这样就不必创建完整的 list,从而节省大量的空间。
在 Python 中,这种一边循环一边计算的机制,称为生成器:generator。
在 Python 中,使用了 yield 的函数被称为生成器(generator)。
跟普通函数不同的是,生成器是一个返回迭代器的函数,只能用于迭代操作,更简单点理解生成器就是一个迭代器。
在调用生成器运行的过程中,每次遇到 yield 时函数会暂停并保存当前所有的运行信息,返回 yield 的值。并在下一次执行 next()方法时从当前位置继续运行。
那么如何创建一个生成器呢?
最简单最简单的方法就是把一个列表生成式的 []
改成 ()
# -*- coding: UTF-8 -*-
gen= (x * x for x in range(10))
print(gen)
输出结果:
at 0x0000000002734A40>
创建 List 和 generator 的区别仅在于最外层的 []
和 ()
。
但是生成器并不真正创建数字列表, 而是返回一个生成器,这个生成器在每次计算出一个条目后,把这个条目“产生” ( yield ) 出来。
生成器表达式使用了“惰性计算” ( lazy evaluation,也有翻译为“延迟求值”,我以为这种按需调用 call by need 的方式翻译为惰性更好一些),只有在检索时才被赋值( evaluated ),所以在列表比较长的情况下使用内存上更有效。
# -*- coding: UTF-8 -*-
def odd():
print ( 'step 1' )
yield ( 1 )
print ( 'step 2' )
yield ( 3 )
print ( 'step 3' )
yield ( 5 )
o = odd()
print( next( o ) )
print( next( o ) )
print( next( o ) )
输出结果:
step 1
1
step 2
3
step 3
5
你想同时迭代多个序列,每次分别从一个序列中取一个元素。你遇到过这样的需求吗?
# -*- coding: UTF-8 -*-
names = ['laingdianshui', 'twowater', '两点水']
ages = [18, 19, 20]
for name, age in zip(names, ages):
print(name,age)
输出结果:
laingdianshui 18
twowater 19
两点水 20
其实 zip(a, b) 会生成一个可返回元组 (x, y) 的迭代器,其中 x 来自 a,y 来自 b。 一旦其中某个序列到底结尾,迭代宣告结束。 因此迭代长度跟参数中最短序列长度一致。注意理解这句话喔,也就是说如果 a , b 的长度不一致的话,以最短的为标准,遍历完后就结束。
利用 zip()
函数,我们还可把一个 key 列表和一个 value 列表生成一个 dict (字典),如下:
# -*- coding: UTF-8 -*-
names = ['laingdianshui', 'twowater', '两点水']
ages = [18, 19, 20]
dict1= dict(zip(names,ages))
print(dict1)
输出结果:
{'laingdianshui': 18, 'twowater': 19, '两点水': 20}
Python 就是一门面向对象的语言,
即一个派生类(derived class)继承基类(base class)的字段和方法。继承也允许把一个派生类的对象作为一个基类对象对待。
它是指对不同类型的变量进行相同的操作,它会根据对象(或类)类型的不同而表现出不同的行为。
“封装”就是将抽象得到的数据和行为(或功能)相结合,形成一个有机的整体(即类);封装的目的是增强安全性和简化编程,使用者不必了解具体的实现细节,而只是要通过外部接口,一特定的访问权限来使用类的成员。
class ClassA():
var1 = 100
var2 = 0.01
var3 = '两点水'
def fun1():
print('我是 fun1')
def fun2():
print('我是 fun1')
def fun3():
print('我是 fun1')
ClassA.fun1()
注意:在类方法上面多了个 @classmethod
,这是干嘛用的呢?
这是用于声明下面的函数是类函数。如果你没使用,是会报错的。
因此类方法,想要调用类属性,需要以下步骤:
@classmethod
声明该方法是类方法。只有声明了是类方法,才能使用类属性cls
, cls 是 class 的缩写,其实意思就是把这个类作为参数,传给自己,这样就可以使用类属性了。cls.变量名
记住喔,无论是 @classmethod
还是 cls
,都是不能省去的。省了都会报错。
主要的不同点有:
@classmethod
声明了,不用声明他是类方法cls
改为 self
实例名 = 类()
的方式实例化对象,为类创建一个实例,然后再使用 实例名.函数()
的方式调用对应的方法 ,使用 实例名.变量名
的方法调用类的属性这里说明一下,类方法的参数为什么 cls
改为 self
?这里什么名字都可以。
你看,把 self
改为 aaaaaaaa
还是可以一样运行的。
只不过使用 cls
和 self
是我们的编程习惯,这也是我们的编程规范。
因为 cls 是 class 的缩写,代表这类 , 而 self 代表这对象的意思。
所以啊,这里我们实例化对象的时候,就使用 self 。
而且 self 是所有类方法位于首位、默认的特殊参数。
除此之外,在这里,还要强调一个概念,当你把类实例化之后,里面的属性和方法,就不叫类属性和类方法了,改为叫实例属性和实例方法,也可以叫对象属性和对象方法。
在这个例子中,我们需要改变类方法,就用到了类的重写。
我们使用了 类.原始函数 = 新函数
就完了类的重写了。
要注意的是,这里的赋值是在替换方法,并不是调用函数。所以是不能加上括号的,也就是 类.原始函数() = 新函数()
这个写法是不对的。
那么如果实例方法改变了,类方法会改变吗?
懂得人都知道就是构造函数
这个 __init__(self)
函数就是初始化函数,也叫构造函数。
初始化函数的写法是固定的格式:中间是 init
,意思是初始化,然后前后都要有【两个下划线】,然后 __init__()
的括号中,第一个参数一定要写上 self
,不然会报错。
构造函数(初始化函数)格式如下:
def __init__(self,[...):
既然一个在创建的时候,会调用构造函数,那么理所当然,这个当一个类销毁的时候,就会调用析构函数。
析构函数语法如下:
def __del__(self,[...):
class ClassName(Base1,Base2,Base3):
.
.
.
在定义类的时候,可以在括号里写继承的类,如果不用继承类的时候,也要写继承 object 类,因为在 Python 中 object 类是一切类的父类。
多继承有一点需要注意的:若是父类中有相同的方法名,而在子类使用时未指定,python 在圆括号中父类的顺序,从左至右搜索 , 即方法在子类中未找到时,从左到右查找父类中是否包含方法。
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
class UserInfo(object):
lv = 5
def __init__(self, name, age, account):
self.name = name
self._age = age
self.__account = account
def get_account(self):
return self.__account
class UserInfo2(UserInfo):
pass
if __name__ == '__main__':
userInfo2 = UserInfo2('两点水', 23, 347073565);
print(userInfo2.get_account())
#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
class UserInfo(object):
lv = 5
def __init__(self, name, age, account):
self.name = name
self._age = age
self.__account = account
def get_account(self):
return self.__account
@classmethod
def get_name(cls):
return cls.lv
@property
def get_age(self):
return self._age
class UserInfo2(UserInfo):
def __init__(self, name, age, account, sex):
super(UserInfo2, self).__init__(name, age, account)
self.sex = sex;
if __name__ == '__main__':
userInfo2 = UserInfo2('两点水', 23, 347073565, '男');
# 打印所有属性
print(dir(userInfo2))
# 打印构造函数中的属性
print(userInfo2.__dict__)
print(UserInfo2.get_name())
#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
class User1(object):
pass
class User2(User1):
pass
class User3(User2):
pass
if __name__ == '__main__':
user1 = User1()
user2 = User2()
user3 = User3()
# isinstance()就可以告诉我们,一个对象是否是某种类型
print(isinstance(user3, User2))
print(isinstance(user3, User1))
print(isinstance(user3, User3))
# 基本类型也可以用isinstance()判断
print(isinstance('两点水', str))
print(isinstance(347073565, int))
print(isinstance(347073565, str))
运行结果:
#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
class User(object):
def __init__(self, name):
self.name = name
def printUser(self):
print('Hello !' + self.name)
class UserVip(User):
def printUser(self):
print('Hello ! 尊敬的Vip用户:' + self.name)
class UserGeneral(User):
def printUser(self):
print('Hello ! 尊敬的用户:' + self.name)
def printUserInfo(user):
user.printUser()
if __name__ == '__main__':
userVip = UserVip('两点水')
printUserInfo(userVip)
userGeneral = UserGeneral('水水水')
printUserInfo(userGeneral)
运行结果:
在 Java 中,有 public (公共)属性 和 private (私有)属性,这可以对属性进行访问控制。
那么在 Python 中有没有属性的访问控制呢?
一般情况下,我们会使用 __private_attrs
两个下划线开头,声明该属性为私有,不能在类地外部被使用或直接访问。在类内部的方法中使用时 self.__private_attrs
。
为什么只能说一般情况下呢?
因为实际上, Python 中是没有提供私有属性等功能的,一切都是靠程序员自觉遵守 Python 的编程规范。
但是 Python 对属性的访问控制是靠程序员自觉的。为什么这么说呢?
看看下面的示例:
方法 | 说明 |
---|---|
__init__ | 构造函数,在生成对象时调用 |
__del__ | 析构函数,释放对象时使用 |
__repr__ | 打印,转换 |
__setitem__ | 按照索引赋值 |
__getitem__ | 按照索引获取值 |
__len__ | 获得长度 |
__cmp__ | 比较运算 |
__call__ | 函数调用 |
__add__ | 加运算 |
__sub__ | 减运算 |
__mul__ | 乘运算 |
__div__ | 除运算 |
__mod__ | 求余运算 |
__pow__ | 乘方 |
当然有些时候我们需要获取类的相关信息,我们可以使用如下的方法:
type(obj)
:来获取对象的相应类型;isinstance(obj, type)
:判断对象是否为指定的 type 类型的实例;hasattr(obj, attr)
:判断对象是否具有指定属性/方法;getattr(obj, attr[, default])
获取属性/方法的值, 要是没有对应的属性则返回 default 值(前提是设置了 default),否则会抛出 AttributeError 异常;setattr(obj, attr, value)
:设定该属性/方法的值,类似于 obj.attr=value;dir(obj)
:可以获取相应对象的所有属性和方法名的列表;在 Python 中,一个 .py 文件就称之为一个模块(Module)。
import module1[, module2[,... moduleN]
from modname import name1[, name2[, ... nameN]]
from···import
和 import
方法有啥区别呢?
from···import
直接导入 version 属性
在 Python 中,有主模块和非主模块之分,当然,我们也得区分他们啊。那么怎么区分主模块和非主模块呢?
这就需要用到 __name__
属性了,这个 ——name——
属性值是一个变量,且这个变量是系统给出的。利用这个变量可以判断一个模块是否是主模块。如果一个属性的值是 __main__
,那么就说明这个模块是主模块,反之亦然。但是要注意了: 这个 __main__
属性只是帮助我们判断是否是主模块,并不是说这个属性决定他们是否是主模块,决定是否是主模块的条件只是这个模块有没有被人调用
示例:
首先创建了模块 lname ,然后判断一下是否是主模块,如果是主模块就输出 main
不是,就输出 not main
,首先直接运行该模块,由于该模块是直接使用,而没有被人调用,所以是主模块,因此输出了 main
然后又创建一个 user_lname 模块,里面只是简单的导入了 lname 模块,然后执行,输出的结果是 not main
,因为 lname 模块被该模块调用了,所以不是主模块,输出结果如图:
为了避免模块名冲突,Python 又引入了按目录来组织模块的方法,称为包(Package)。比如最开始的例子,就引入了包,这样子做就算有相同的模块名,也不会造成重复,因为包名不同,其实也就是路径不同。如下图,引入了包名后, lname.py 其实变成了 com.Learn.module.nameattributes.lname
仔细观察的人,基本会发现,每一个包目录下面都会有一个 __init__.py
的文件,为什么呢?
因为这个文件是必须的,否则,Python 就把这个目录当成普通目录,而不是一个包 。 __init__.py
可以是空文件,也可以有Python代码,因为 __init__.py
本身就是一个模块,而它对应的模块名就是它的包名。
学习过 Java 的同学都知道,Java 的类里面可以给方法和属性定义公共的( public )或者是私有的 ( private ),这样做主要是为了我们希望有些函数和属性能给别人使用或者只能内部使用。
通过学习 Python 中的模块,其实和 Java 中的类相似,那么我们怎么实现在一个模块中,有的函数和变量给别人使用,有的函数和变量仅仅在模块内部使用呢?
在 Python 中,是通过 _
前缀来实现的。
正常的函数和变量名是公开的(public),可以被直接引用,比如:abc,ni12,PI等;类似__xxx__
这样的变量是特殊变量,可以被直接引用,但是有特殊用途,比如上面的 __name__
就是特殊变量,还有 __author__
也是特殊变量,用来标明作者。
注意,我们自己的变量一般不要用这种变量名;类似 _xxx
和 __xxx
这样的函数或变量就是非公开的(private),不应该被直接引用,比如 _abc
,__abc
等;
这里是说不应该,而不是不能。因为 Python 种并没有一种方法可以完全限制访问 private 函数或变量,但是,从编程习惯上不应该引用 private 函数或变量。
方法 | 说明 |
---|---|
__cmp__(self, other) | 如果该方法返回负数,说明 self < other ; 返回正数,说明 self > other ; 返回 0 说明 self == other 。强烈不推荐来定义 __cmp__ , 取而代之, 最好分别定义 __lt__ , __eq__ 等方法从而实现比较功能。 __cmp__ 在 Python3 中被废弃了。 |
__eq__(self, other) | 定义了比较操作符 == 的行为 |
__ne__(self, other) | 定义了比较操作符 != 的行为 |
__lt__(self, other) | 定义了比较操作符 < 的行为 |
__gt__(self, other) | 定义了比较操作符 > 的行为 |
__le__(self, other) | 定义了比较操作符 <= 的行为 |
__ge__(self, other) | 定义了比较操作符 >= 的行为 |
#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
from enum import Enum
Month = Enum('Month', ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'))
# 遍历枚举类型
for name, member in Month.__members__.items():
print(name, '---------', member, '----------', member.value)
# 直接引用一个常量
print('\n', Month.Jan)
运行结果:
我们使用 Enum
来定义了一个枚举类。
上面的代码,我们创建了一个有关月份的枚举类型 Month ,这里要注意的是构造参数,第一个参数 Month 表示的是该枚举类的类名,第二个 tuple 参数,表示的是枚举类的值;当然,枚举类通过 __members__
遍历它的所有成员的方法。
注意的一点是 , member.value
是自动赋给成员的 int
类型的常量,默认是从 1 开始的。
而且 Enum 的成员均为单例(Singleton),并且不可实例化,不可更改
#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
from enum import Enum, unique
Enum('Month', ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'))
# @unique 装饰器可以帮助我们检查保证没有重复值
@unique
class Month(Enum):
Jan = 'January'
Feb = 'February'
Mar = 'March'
Apr = 'April'
May = 'May'
Jun = 'June'
Jul = 'July'
Aug = 'August'
Sep = 'September '
Oct = 'October'
Nov = 'November'
Dec = 'December'
if __name__ == '__main__':
print(Month.Jan, '----------',
Month.Jan.name, '----------', Month.Jan.value)
for name, member in Month.__members__.items():
print(name, '----------', member, '----------', member.value)
输出的结果:
通过上面的例子,可以知道枚举模块定义了具有迭代 (interator) 和比较(comparison) 功能的枚举类型。 它可以用来为值创建明确定义的符号,而不是使用具体的整数或字符串。
元类就是用来创建类的。也可以换个理解方式就是:元类就是类的类。
因为类也是对象,所以我们可以在程序运行的时候创建类。
Python 是动态语言。
动态语言和静态语言最大的不同,就是函数和类的定义,不是编译时定义的,而是运行时动态创建的。
首先我们新建一个 hello.py
的模块,然后定义一个 Hello 的 class ,
class Hello(object):
def hello(self, name='Py'):
print('Hello,', name)
然后在另一个模块中引用 hello 模块,并输出相应的信息。
#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
from com.twowater.hello import Hello
h = Hello()
h.hello()
print(type(Hello))
print(type(h))
输出结果:
type()
函数可以查看一个类型或变量的类型,Hello
是一个 class
,它的类型就是 type
,而 h
是一个实例,它的类型就是 com.twowater.hello.Hello
。
前面的 com.twowater
是我的包名,hello
模块在该包名下。
type()
函数不仅可以返回一个对象的类型,也可以创建出新的类型。在 Python 中,类也是对象,你可以动态的创建类。
首先我们来了解下 __metaclass__
属性
metaclass,直译为元类,简单的解释就是:
当我们定义了类以后,就可以根据这个类创建出实例,所以:先定义类,然后创建实例。
但是如果我们想创建出类呢?
那就必须根据metaclass创建出类,所以:先定义metaclass,然后创建类。
连接起来就是:先定义metaclass,就可以创建类,最后创建实例。
所以,metaclass 允许你创建类或者修改类。
换句话说,你可以把类看成是 metaclass 创建出来的“实例”。
class MyObject(object):
__metaclass__ = something…
[…]
流程图:
示例:
class Foo(Bar):
pass
首先判断 Foo 中是否有 __metaclass__
这个属性?如果有,Python 会在内存中通过 __metaclass__
创建一个名字为 Foo 的类对象(注意,这里是类对象)。如果 Python 没有找到__metaclass__
,它会继续在 Bar(父类)中寻找__metaclass__
属性,并尝试做和前面同样的操作。如果 Python在任何父类中都找不到 __metaclass__
,它就会在模块层次中去寻找 __metaclass__
,并尝试做同样的操作。如果还是找不到`
metaclass` ,Python 就会用内置的 type 来创建这个类对象。
其实 __metaclass__
就是定义了 class 的行为。类似于 class 定义了 instance 的行为,metaclass 则定义了 class 的行为。可以说,class 是 metaclass 的 instance。
现在,我们基本了解了 __metaclass__
属性,但是,也没讲过如何使用这个属性,或者说这个属性可以放些什么?
答案就是:可以创建一个类的东西。那么什么可以用来创建一个类呢?type,或者任何使用到 type 或者子类化 type 的东东都可以。
十四、待续