本文最后更新于:2019 , 八月 19日 星期一, 4:32 下午
0x00 初识类
python中一切皆对象
创建类
'''
class 类名:
'类的文档'
类体
'''
class Data:
pass
关键字:class
访问属性
格式:类名.属性
类名的作用:操作属性,查看属性
#(注:这只是示范,实际操作请勿使用中文)
class Data:
属性 = 'a'
print(Data.a)
实例化
格式:对象名 = 类名(参数)
类名()
就是实例化,会自动触动__init__
函数的运行,可以用类为每个实例定制自己的特征
class Person:
def __init__(self,*args):
self.name = args[0]
self.age = args[1]
self.heg = args[2]
alex = Person('Psycho',0,'???')
print(alex.name)
----------------------------------
'''
对象 = 类名()
过程:
类名() 会创造出一个对象,创建一个self变量
调用__inti__方法,类名()里面的参数会被这里接收
执行__init__方法
返回self
'''
实例化方法
格式:类名.方法名(对象名)
class Person:
def __init__(self,*args):
self.name = args[0]
self.age = args[1]
self.heg = args[2]
def walk(self):
print('%s'%self.name)
alex = Person('Psycho',0,'???')
print(alex.name)
Person.walk(alex)
__init__
方法
这个方法一般用于初始化类
但是,当实例化一个类的时候,并不是第一个被调用的,第一个被调用的是__new__
Tips:对当前对象的实例的一些初始化,没有返回值
设置对象的初始属性:self.属性 = 形参
在创建对象时,使用类名(属性1,属性2...)
进行调用
class Person:
def __init__(self,name,age):
self.name = name
self.age = age
info = Person('alex','22')
self
init方法的第一参数永远是self,表示创建类的实例化本身
因此,在init方法内部,就可以把各种属性绑定到self
self.name
:表示某类的属性变量
self.name = name 就是把外部传来的参数name赋值给某类自己的属性变量
类属性
类的属性存储地址:
dir(类名)
类名.__dict__:查出的是一个字典,key为属性名,value为属性值
---------------------
类名.__name__# 类的名字(字符串)
类名.__doc__# 类的文档字符串
类名.__base__# 类的第一个父类
类名.__bases__# 类所有父类构成的元组
类名.__dict__# 类的字典属性
类名.__module__# 类定义所在的模块
类名.__class__# 实例对应的类(仅新式类中)
其他内置方法
__str__
如果直接print(object)打印对象,会看到创建出来的对象在内存中的地址
当使用print(object)输出对象的时候,只要对象的类中定义了__str__(self)
方法,就会打印该方法return
的信息描述
class Person:
def __str__(self):
return "这是个信息模块"
def __init__(self,name,age):
self.name = name
self.age = age
info = Person('alex','22')
print(info)
__del__
析构方法,当对象在内存中被释放时,自动触发执行
在删除一个对象之前,进行一些收尾工作
class Person:
def __str__(self):
return "这是个信息模块"
def __init__(self, name, age):
self.name = name
self.age = age
def __del__(self):
print('关闭对象')
info = Person('alex', '22')
__repr__
返回一个可以用来表示对象的可打印字符串__repr__
是__str__
的备胎,但__str__
不能做__repr__
的备胎
如果__str__
方法有,那么它返回的必定一个字符串
class A:
def __init__(self,name,age):
self.name = name
self.age = age
def __repr__(self):
return str(self.__dict__)
# def __str__(self):
# return '%s,%s'%(self.name,self.age)
a = A('xxx',20)
print(a)
Ps:%r,repr()都是走
__repr__
与__str__
的区别:
__str__()
:返回用户看到的字符串__repr__()
:返回程序开发者看到的字符串
内置的方法有很多,不一定全部都在object中
__call__
一个对象后面加括号,触发执行
class A:
def __init__(self,name):
pass
def __call__(self):
print('执行此处')
a = A('alex')
__getattr__
定义当用户试图获取一个不存在的属性时的行为,适用于对普通拼写错误的获取和重定向,对获取一些不建议的属性时给出警告或者处理一个AttributeError,只有当调用不存在的属性的时候才会被返回
class Student(object):
def__getattr__(self, attrname):
ifattrname =="age":
return40
else:
raiseAttributeError, attrname
x =Student()
print(x.age) #40
print(x.name) #error text omitted.....AttributeError, name
__setattr__
是一个封装的解决方案,无论属性是否存在,它都允许你定义对属性的赋值行为,以为这你可以对属性的值进行个性定制,实现__setattr__
时要避免”无限递归”错误
当试图对象的item特性赋值的时候将会被调用
# -*- coding:utf-8 -*-
class Student:
def __getattr__(self, item):
return item + ' is not exits'
def __setattr__(self, key, value):
self.__dict__[key] = value
def __getitem__(self, item):
return self.__dict__[item]
def __setitem__(self, key, value):
self.__dict__[key] = value
s = Student()
print(s.name) # 调用__getattr__方法 输出'name is not exits'
s.age = 1 # 调用__setattr__ 方法
print(s.age) # 输出 1
print(s['age']) # 调用 __getitem__方法 输出1
s['name'] = 'tom' # 调用 __setitem__ 方法
print(s['name']) # 调用 __getitem__ 方法 输出 'tom'
__delattr__
与__setattr__
相同,删除一个属性
class A(object):
def __delattr__(self, *args, **kwargs):
print 'call func del attr'
return object.__delattr__(self, *args, **kwargs)
__getattribute__
定义了你的属性被访问时的行为,在支持__getattribute__
的python版本,调用__getattr__
前必定会调用__getattribute__
,不要尝试实现__getattribute__
容易出bug
class Tree(object):
def __init__(self,name):
self.name = name
self.cate = "plant"
def __getattribute__(self,obj):
print("哈哈")
return object.__getattribute__(self,obj)
aa = Tree("大树")
print(aa.name)
item系列
__getitem__
当访问不存在的属性时会调用该方法
可以让对象实现迭代功能,以及p[key]取值
# data['key']取值
class DataTest:
def __init__(self, name,id):
self.name = name
self.id = id
def __getitem__(self, item):
return self.__dict__[item]
data = DataTest('alex','1')
print(data['name'])
# 实现迭代功能
class DataTest:
def __init__(self, name,id):
self.name = name
self.id = id
def __getitem__(self, item):
return self.name[item]
data = DataTest(['alex','lie','psycho'],'1')
for i in data:
print(i)
__setitem__
每当属性被赋值的时候都会调用该方法,因此不能在该方法内(self.name = value)赋值,否则会死循环!
class DataTest:
def __init__(self, name,id):
self.name = name
self.id = id
def __getitem__(self, item):
return self.__dict__[item]
def __setitem__(self, key, value):
self.__dict__[key]=value
data = DataTest(['alex','lie','psycho'],'1')
data['alex'] = '女'
print(data['alex'])
__delitem__
当删除属性时调用该方法
lass DataTest:
def __init__(self, name,id):
self.name = name
self.id = id
def __getitem__(self, item):
return self.__dict__[item]
def __setitem__(self, key, value):
self.__dict__[key]=value
def __delitem__(self, key):
del self.__dict__[key]
data = DataTest(['alex','lie','psycho'],'1')
data['alex'] = '女'
print('删除前:%s'%data.__dict__)
del data['alex']
print('删除后:%s'%data.__dict__)
__new__
构造方法:创建一个对象(只负责创建)
class DataTest:
__instance = False
def __init__(self, name,id):
self.name = name
self.id = id
def __new__(cls, *args, **kwargs):
if cls.__instance:
return cls.__instance
cls.__instance = object.__new__(cls)
return cls.__instance
data = DataTest('alex','1')
nas = DataTest('nice','2')
print(data)
print(nas)
__eq__
当判断两个对象是否相等时,触发
class DataTest:
def __init__(self, name,id):
self.name = name
self.id = id
def __eq__(self, other):
if self.__dict__ == other.__dict__:
return True
else:
return False
data = DataTest('alex','1')
nas = DataTest('nice','2')
print(data == nas)
__len__
返回元素的个数
如果一个类表现得像一个list,要获取有多少个元素,就得用len()函数,要让len()函数正常工作,类必须提供一个特殊方法len()
class DataTest:
__instance = False
def __init__(self, name,id):
self.name = name
self.id = id
def __len__(self):
return len(self.name)
data = DataTest(['alex','nice'],'1')
print(len(data.name))
__hash__
如果定义可变对象的类实现了__eq__
放阿飞,就不要再实现__hash__
方法,否则这个对象的hash值发生变化会导致被放在错误的哈希中
class DataTest:
def __init__(self):
self.name = 'alex'
self.id = '2'
def __hash__(self):
return hash(str(self.name)+str(self.id))
a = DataTest()
print(hash(a))
面向对象的三大特性
继承
:实现代码重用多态
:不同的对象调用相同方法,产生不同的执行结果封装
:根据职责将属性和方法封装到一个抽象的类中
0x02 继承
继承是一种创新类的方式,实现代码的重用,相同的代码不需要重复的编写
继承的传递性
:子类
拥有父类
的所有方法
和属性
及父类的的父类
中封装的所有属性和方法
Ps:继承父类的时候,前提条件必须有那个类的存在!
Ps:如果在自己的类中有该方法,先用自己的,没有再从父类中寻找
1. 继承分为:
- 单继承
- 多继承
2. 通过继承创建的新类:
- 子类
- 派生类
3. 被继承的类:
- 基类
- 父类
- 超类
查看继承:__bases__
单继承
继承的语法:
class 类名(父类名):
pass
子类拥有
父类的所有方法
和属性
class Arm:
def __init__(self,name,aggr,hp):
self.name = name
self.aggr = aggr
self.hp = hp
class Person(Arm):
pass
class Dog(Arm):
pass
jin = Dog('jin',500,20)
print(jin.name)
多继承
子类可以拥有多个父类,并且具有所有父类的属性和方法
class 类名(父类1,父类2...):
pass
多继承中,我们子类的对象调用一个方法,默认是就近原则 经典类中 深度优先 新式类中 广度优先 2.7 新式类和经典类共存,新式类要继承object python3 只有新式类
class ParentClass1: #定义父类
pass
class ParentClass2: #定义父类
pass
class SubClass1(ParentClass1): #单继承,基类是ParentClass1,派生类是SubClass
pass
class SubClass2(ParentClass1,ParentClass2): #python支持多继承,用逗号分隔开多个继承的类
pass
Ps:父类之间存在同名的属性或方法,应该尽量避免使用多继承,容易产生混淆
重用性
在程序开发的过程中,定义了一个A类与一个B类,但是这两个类的大部分内容都相同 我们可以通过继承的方式,让B类继承A的所有属性,实现代码重用
class Arm:
def __init__(self,name,aggr,hp):
self.name = name
self.aggr = aggr
self.hp = hp
class Dog(Arm):
pass
jin = Dog('jin',500,20)
print(jin.name)
方法的重写与派生
当父类的方法实现不能满足子类的需求时,可以对方法进行重写
重写父类的方法有两种情况:
- 覆盖父类方法
- 对父类方法进行扩展
覆盖父类
具体的实现方式,就相当于在子类中定义了一个和父类同名的方法并且实现
重写之后,在运行时,只会调用子类中重写的方法,而不再会调用父类封装的方法
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def ack(self):
print('wu')
class name(Person):
def ack(self):
print('%s ackti'%self.name)
info = name('alex',20)
info.ack()
print(info.age)
重写父类方法(扩展)
在子类执行父类的方法也可以直接用super()
方法
super本质:不是单纯找父类,而是根据调用者的节点位置的广度优先顺序来的
格式:super().父类方法
class Person:
def __init__(self,name,age):
self.name = name
self.age = age
class Arm(Person):
def __init__(self,name,age,kind):
super().__init__(name,age)
self.kind = kind
sex = Arm('alex',20,50)
print(sex.kind)
派生属性
派生属性/方法
:父类中没有的属性或方法,在子类中出现的
一旦重新定义了自己的属性且父类重名,那么调用新的属性时,就以自己为准
class Person:
def __init__(self,name,age):
self.name = name
self.age = age
class Arm(Person):
def __init__(self,name,age,kind):
Person.__init__(self,name,age)
self.kind = kind
sex = Arm('alex',50,20)
print(sex.kind)
只要是子类的对象调用,子类中有的名字一定用子类的,子类中没有才找父类的
如果父类也没有 报错
如果父类 子类都有,用子类的,如果还想用父类的,需要单独调用父类的,需要自己传self参数
class Arm:
def __init__(self,name,aggr,hp):
self.name = name
self.aggr = aggr
self.hp = hp
def eat(self):
print('吃药回血')
self.hp += 100
class Dog(Arm):
def __init__(self,name,aggr,hp,kind):
Arm.__init__(self,name,aggr,hp)
self.kind = kind
def eat(self):
Arm.eat(self)
self.Thear = 2
def bite(self,person):
person.hp -= self.aggr
class Person(Arm):
def __init__(self,name,aggr,hp,sex):
Arm.__init__(self,name,aggr,hp)
self.sex = sex
self.money = 0
def attack(self,dog):
dog.hp -= self.aggr
def get_weapon(self,weapon):
if self.money >= weapon.price:
self.money -= weapon.price
self.weapon = weapon
self.aggr += weapon.aggr
else:
print("余额不足,请充值")
jin = Dog('jin',500,20,10)
jin.eat()
MRO(方法搜索顺序)
python针对类提供的一个内置属性,可以查看方法的搜索顺序
主要用于在多继承时判断,方法,属性的调用路径
从左到右的顺序查找,在当前类中找到方法就执行,不再搜索
没有找到,继续查找下一个类
最终没找到,报错
class Person:
def __init__(self,name,age):
self.name = name
self.age = age
class Arm(Person):
def __init__(self,name,age,kind):
super().__init__(name,age)
self.kind = kind
print(Arm.__mro__)
0x03 封装
广义上的面向对象封装:代码的保护,面向对象的思想本身就是一种,只让自己的对象能调用自己类中的方法
狭义封装:面向对象的三大特性之一,属性和方法都隐藏起来
封装的三种方式:
- public(对外公开,不封装)
- protected(对外不公开,对子类公开)
- private(对谁都公开)
python中一切皆对象
类是一个特殊的对象 – 类对象,使用一个类可以创建出很多歌对象实例
处了封装实例的属性和方法外,类对象还可以拥有自己的属性和方法
类属性
给类对象中定义的属性
通常用来记录与这个类相关的特征
类属性不会用于记录具体对象的特征
调用:类名.
的方式
# 计算人数
class Person:
count = 0
def __init__(self, name):
self.name = name
Person.count += 1
name1 = Person('alex')
print(Person.count)
属性的获取机制:向上查找机制
- 首先在对象内部查找对象属性
- 没有找到就会向上寻找类属性
访问类属性有两种方式:
类名.类属性
对象.类属性
(不推荐)
Ps:如果使用
对象.类属性 = 值
赋值语句,只会给对象添加一个属性,而不会影响到类属性的值
私有方法和属性
私有属性和方法是对象的隐私,不对外公开,外界及子类都不能直接访问
通常用于做一些内部的事情
- 对象的私有属性
- 类中的私有方法
- 类中静态私有属性
定义私有属性与方法:__
属性或方法
只要在类的内部使用私有属性,就会自动带上__类名
Ps:所有的私有,都不能在类的外部使用
在外部调用的方法:对象._类名__属性
(不推荐使用,仅供了解)
class Person:
__manger = 'admin' # 静态私有属性
def __init__(self,username,passwod):
self.username = username
self.__password = passwod # 私有属性
def __mange(self): # 私有方法
print("管理员登录")
def login(self):
if self.__password != self.username:
return ("请输入正确的账号密码")
else:
if self.username == self.__manger:
return self.__mange()
else:
return ("登录成功")
username = 'admin'
password = 'admin'
win = Person(username,password)
login = win.login()
print(login)
property
内部装饰器,在面向对象中使用
把方法伪装成属性
:@property
Ps:装饰器中不能跟任何参数
class Person:
def __init__(self,name):
self.name = name
@property
def work(self):
return '111'
x = Person('admin')
print(x.work)
修改@property
伪装的方法
Ps:现有
@property
再有@方法名.setter
,再创建一个一样的方法
@方法名.setter
:把被装饰的方法变成属性来赋值
# 例子1
class Person:
def __init__(self, name):
self.__name = name
@property
def name(self):
return self.__name
@name.setter
def name(self, new_name):
self.__name = new_name
tiger = Person('nice')
print(tiger.name)
tiger.name = 'fuck'
print(tiger.name)
###################################################
# 例子2
class Person:
def __init__(self, name):
self.__name = name
@property
def name(self):
return self.__name
@name.deleter
def name(self):
del self.__name
tiger = Person('nice')
print(tiger.name)
del tiger.name
print(tiger.name)
# deleter和del互相关联了
classmethod
method
:方法
classmethod
:类方法
Ps:把一个方法,变成一个类中的方法,这个方法就直接可以被类调用,不需要依托任何对象
当这个方法的操作只涉及静态属性的时候,就应该使用classmethod
来装饰这个方法
对应的方法不需要实例化,不用self参数,当第一个参数需要是表示自身类的cls参数
,可以来调用类的属性和方法及实例化对象等
调用:类名.方法
,也可以通过cls访问类的属性和其他的类方法
@classmethod
def 类方法名(cls):
pass
# 例子
class Person:
__discount = 0.8
def __init__(self,price):
self.__price = price
@property
def price(self):
return self.__price * Person.__discount
@classmethod
def price_discount(cls,new_discount):
cls.__discount = new_discount
apple = Person(5)
print(apple.price)
Person.price_discount(1)
print(apple.price)
staticmethod
staticmethod
:静态方法
在完全面向对象的程序中,如果一个函数,即和对象没有关系,也和类没有关系,就将一个函数变成一个静态方法
调用:类名.方法
@staticmethod
def 静态方法名():
pass
class Login:
def __init__(self,username,password):
self.__username = username
self.__password = password
def login(self):
pass
@staticmethod
def get_input():
user = input('username:')
pwd = input('password:')
Login(user,pwd)
Login.get_input()
Ps:类方法和静态方法 都是类调用的
对象可以调用类方法和静态方法,一般情况下,推荐用类名调用
类方法 有一个默认参数 cls代表这个类
0x04 多态
python天生支持多态,多态指的是同一种事物的多种状态
多态不同的子类对象调用相同的父类方法,产生不同的执行结果
- 增加代码的灵活度
- 以继承和重写父类方法为前提
- 是调用方法的技巧,不会影响到类的内部设计
0x05 组合
一个对象的属性值是另一个类的对象
在一个类中以另外一个类的对象作为数据属性,成为类的组合
# 例子1
class Date:
def __init__(self,year,mon,day):
self.year=year
self.mon=mon
self.day=day
def birth_info(self):
print("The birth is %s-%s-%s"%(self.year,self.mon,self.day))
class People:
def __init__(self,name,age,year,mon,day):
self.name=name
self.age=age
self.birth=Date(year,mon,day)
def walk(self):
print("%s is walking"%self.name)
class Teacher(People):
def __init__(self,name,age,year,mon,day,course):
People.__init__(self,name,age,year,mon,day)
self.course=course
def teach(self):
print("%s is teaching"%self.name)
class Student(People):
def __init__(self,name,age,year,mon,day,group):
People.__init__(self,name,age,year,mon,day)
self.group=group
def study(self):
print("%s is studying"%self.name)
t1=Teacher("alex",28,1989,9,2,"python")
s1=Student("jack",22,1995,2,8,"group2")
print(t1.birth.birth_info())
# 例子2
class BirthDate:
def __init__(self,year,month,day):
self.year=year
self.month=month
self.day=day
class Couse:
def __init__(self,name,price,period):
self.name=name
self.price=price
self.period=period
class Teacher:
def __init__(self,name,gender,birth,course):
self.name=name
self.gender=gender
self.birth=birth
self.course=course
def teach(self):
print('teaching')
p1=Teacher('egon','male',
BirthDate('1995','1','27'),
Couse('python','28000','4 months')
)
print(p1.birth.year,p1.birth.month,p1.birth.day)
print(p1.course.name,p1.course.price,p1.course.period)
'''
运行结果:
1 27
python 28000 4 months
'''
0x06 单例设计模式
设计模式:前任工作的总结和提炼
,为了可重用代码,让代码更容易被人理解
单列设计模式:机制:当你第一次实例化这个类的时候,就创建一个实例化的对象,当你之后再实例化,就用之前创建的对象,把原来的重复的属性覆盖,而不同的属性则继承下来
重写__new__
方法
利用__new__
方法
使用类名()创建对象时,Python解释器首先会调用__new__
方法为对象分配空间
主要作用:
- 在内存中为对象分配空间
- 返回对象的引用
Python的解释器获得对象的引用后,将引用作为第一个参数,传递给__init__
方法
重写
__new__
方法的代码非常固定一定要
return super().__new__(cls)
否则Python解释器得不到分配了空间的对象引用,就会调用对象的初始化方法
在调用时需要主动传递
cls
参数(静态方法)
*
:多参数元组形式
**
:多参数字典形式
# 例子1
class Person:
def __new__(cls, *args, **kwargs):
print('test')
instance = super().__new__(cls)
return instance
def __init__(self, name):
self.name = name
name1 = Person('alex')
# 例子2
class DataTest:
__instance = False
def __init__(self, name,id):
self.name = name
self.id = id
def __new__(cls, *args, **kwargs):
# 判断属性是否赋值
if cls.__instance:
return cls.__instance
cls.__instance = object.__new__(cls)
return cls.__instance
data = DataTest('alex','1')
nas = DataTest('nice','2')
print(data)
print(nas)
初始化方法执行一次
让初始化方法只执行一次:
- 标记是否执行过初始化方法
- 在
__init__
进行判断 - 然后修改标记
class Person:
info = False
__instance = False
def __new__(cls, *args, **kwargs):
# 判断属性是否赋值
if cls.__instance:
return cls.__instance
cls.__instance = object.__new__(cls)
return cls.__instance
def __init__(self, name):
if Person.info:
return
self.name = name
Person.info = True
name1 = Person('alex')
print(name1)
name2 = Person('alex2')
print(name2)
0x07 异常
异常和错误的概念
错误:
语法错误,逻辑错误
异常
:程序运行时发生的错误信号 程序一旦发生错误,就从错误的位置停下来,不在继续执行后面的内容
Ps:抛出异常的一瞬间,程序并不会立即终止,而是会把异常传递给函数/方法的调用一方,如果传递到主程序,任然没有异常处理,程序才会被终止
基础语法
try:
被检测的代码块
except:
try中一旦检测到异常,就会执行这个位置的逻辑
异常类
异常名称 | 描述 |
---|---|
BaseException | 所有异常的基类 |
SystemExit | 解释器请求退出 |
KeyboardInterrupt | 用户中断执行(通常是输入^C) |
Exception | 常规错误的基类 |
StopIteration | 迭代器没有更多的值 |
GeneratorExit | 生成器(generator)发生异常来通知退出 |
StandardError | |
ArithmeticError | 所有数值计算错误的基类 |
FloatingPointError | 浮点计算错误 |
OverflowError | 数值运算超出最大限制 |
ZeroDivisionError | 除(或取模)零 (所有数据类型) |
AssertionError | 断言语句失败 |
AttributeError | 对象没有这个属性 |
EOFError | 没有内建输入,到达EOF 标记 |
EnvironmentError | 操作系统错误的基类 |
IOError | 输入/输出操作失败 |
OSError | 操作系统错误 |
WindowsError | 系统调用失败 |
ImportError | 导入模块/对象失败 |
LookupError | 无效数据查询的基类 |
IndexError | 序列中没有此索引(index) |
KeyError | 映射中没有这个键 |
MemoryError | 内存溢出错误(对于Python 解释器不是致命的) |
NameError | 未声明/初始化对象 (没有属性) |
UnboundLocalError | 访问未初始化的本地变量 |
ReferenceError | 弱引用(Weak reference)试图访问已经垃圾回收了的对象 |
RuntimeError | 一般的运行时错误 |
NotImplementedError | 尚未实现的方法 |
SyntaxError | Python 语法错误 |
IndentationError | 缩进错误 |
TabError | Tab 和空格混用 |
SystemError | 一般的解释器系统错误 |
TypeError | 对类型无效的操作 |
ValueError | 传入无效的参数 |
UnicodeError | Unicode 相关的错误 |
UnicodeDecodeError | Unicode 解码时的错误 |
UnicodeEncodeError | Unicode 编码时错误 |
UnicodeTranslateError | Unicode 转换时错误 |
Warning | 警告的基类 |
DeprecationWarning | 关于被弃用的特征的警告 |
FutureWarning | 关于构造将来语义会有改变的警告 |
OverflowWarning | 旧的关于自动提升为长整型(long)的警告 |
PendingDeprecationWarning | 关于特性将会被废弃的警告 |
RuntimeWarning | 可疑的运行时行为(runtime behavior)的警告 |
SyntaxWarning | 可疑的语法的警告 |
UserWarning | 用户代码生成的警告 |
Tips:
异常类只能用来处理指定的异常情况,如果非指定异常则无法处理
s1 = 'hello'
try:
int(s1)
except IndexError as e:
print e
多分支与万能异常
多分支
s1 = 'hello'
try:
int(s1)
except IndexError as e:
print(e)
except KeyError as e:
print(e)
except ValueError as e:
print(e)
万能异常(Exception):
捕获任意异常
try: 被检测的代码块 except Exception as e: print(e)
主动抛出异常:根据应用程序特有的业务需求主动抛出异常
- 创建一个
Exception
的对象 - 使用
raise
关键字抛出异常对象
raise
:显示引发异常,一旦执行了raise语句,raise后面的语句将不能执行
raise [Exception [, args [, traceback]]]
def input_pwd():
pwd = input("请输入密码")
if len(pwd) >= 8:
return pwd
print("错误")
ex = Exception("密码长度不够8位")
raise ex
try:
print(input_pwd())
except Exception as result:
print(result)
如果你想要的效果是,对于不同的异常我们需要定制不同的处理逻辑,那就需要用到多分支
s1 = 'hello'
try:
int(s1)
except IndexError as e:
print(e)
except KeyError as e:
print(e)
except ValueError as e:
print(e)
except Exception as e:
print(e)
finally
无论异常是否发生,在程序结束前,finally中的语句都会被执行 finally和return相遇的时候,依然会执行 函数里做异常处理用,不管是否异常去做一些收尾工作
try:
被检测的代码块
finally:
print('xxxx')
# -*- coding: UTF-8 -*-
def tryTest():
try:
demo = input("input a number:")
x = 1.0/int(demo)
print x
return 1
except Exception, e:
print e
return 0
finally:
print "this is a finally test"
if __name__ == '__main__':
result = tryTest()
print result
0x08 模块
模块
:一个模块就是一个包含了python定义和声明的文件,文件名就是模块名+.py的后缀
在模块中定义的全局变量,函数,类都是提供给外界直接使用的工具
import
加载的模块分为四个通用类别:
- 使用python编写的代码(.py文件)
- 已被编译为共享库或DLL的C或C++扩展
- 包好一组模块的包
- 使用C编写并链接到python解释器的内置模块
模块可以包含可执行的语句和函数定义,这些语句的目的是初始化模块 它们只在模块名第一遇到导入import语句时才执行 为了防止你重复导入
python的优化手段:第一次导入后就将模块加载到内存了,后续的iimport语句仅是对已加载大内存中的模块对象增加一个引用,不会重新执行模块内的语句
Ps:从
sys.modules
中找到当前已经加载的模块,sys.modules
是一个字典,内部包含模块名与模块对象的映射,该字典决定了导入模块时是否需要重新导入
导入模块
每个模块都是一个独立的名称空间,定义在这个模块中的函数,把这个模块的名称当做全局名称空间 这样我们在编写自己的模块时,就不用担心我们定义在自己模块中全局变量会被导入时,与使用者的全局变量冲突
原则:每一个文件都应该是可以被导入的,在导入文件时,文件中所有没有任何缩进的代码,都会被执行一遍
首次导入模块时会做的三件事
- 为源文件创建新的名称空间
- 在新建命名空间中执行模块中包含的代码
- 创建名称***来引用该命名空间
import
导入import 模块1,模块2
导入之后:通过
模块名.
使用模块提供的工具 – 全局变量,函数,类Ps:在导入模块时,每个导入应独占一行
from...import...
导入from 模块 import 方法 # 例子 from time import sleep
如果希望从某一个模块中,导入部分的函数功能,就可以使用
直接使用模块提供的函数
Ps:如果两个模块,存在同名的函数,那么导入模块的函数,会覆盖掉先导入的函数
from...import *
(仅限了解)from time import *
导入所有的方法和属性
不推荐使用,因为函数重名并有任何的提示,出现问题不好debug
指定模块别名
使用as
在模块名太长的情况下,可以使用as
指定模块的名称
import 模块 as 模块别名
#例子
import time as f
Ps:模块别名应该符合大驼峰命名规则,
form...import
导入方式同样适用
模块搜索顺序
python的解释器在导入模块时:
- 搜索当前目录指定模块名的文件,如果有就直接导入
- 没有,再搜索系统目录
在开发时,给文件起名,不要和系统的模块文件重名
__file__
:查看模块的完整路径
import random
print(random.__file__)
在模块文件中测试
使用__name__
属性,记录着一个字符串
__name__
:在其他脚本调用(导入)的的时候name显示的是模块的名字 而在原模块调用的时候显示的是__main__
if __name__ == '__main__':
pass
通过上面的方式,python就可以分清楚哪些是主函数,进入函数执行,并且可以调用其他模块的各个函数等等
0x09 包(Package)
包
:一个包含多个模块的特殊目录,目录下有一个特殊的文件__init__.py
包即模块 import导入文件时,产生名称空间中的名字来源于文件,import包,产生的名称空间的名字同样来源于文件,即包下的__init__.py
,导入包本质就是在导入该文件
在python3中,即使包下没有__init__.py
文件,import包任然不会报错
在python2中,包下一定要有该文件,否则import包会报错
Ps:导入包遵循的原则:
凡是在导入时带点的,点的左边都必须是一个包,否则非法
,item.subitem.subsubitem 对于导入后,在使用时就没有这种限制了,点的左边可以是包,模块,函数(它们都可以用点的方式调用自己的属性)
import 包名
from...import...
导入
from glance.api import policy
Ps:from后import导入的模块,必须是明确的一个不能带点,否则会有语法错误
_init_.py
文件 不管是哪种方式,只要是第一次导入包或是包的任意其他部分,都会依次执行包下的__init__.py
这个文件可以为空,但是也可以存放一些初始化包的代码
绝对导入和相对导入
绝对导入
格式:from glance.api import policy # 路径图 glance/ ├── __init__.py from glance import api from glance import cmd from glance import db ├── api │ ├── __init__.py from glance.api import policy from glance.api import versions │ ├── policy.py │ └── versions.py ├── cmd from glance.cmd import manage │ ├── __init__.py │ └── manage.py └── db from glance.db import models ├── __init__.py └── models.py
相对导入
.
或..
的方式为起始格式:from .api import policy # 路径图 glance/ ├── __init__.py from . import api #.表示当前目录 from . import cmd from . import db ├── api │ ├── __init__.py from . import policy from . import versions │ ├── policy.py │ └── versions.py ├── cmd from . import manage │ ├── __init__.py │ └── manage.py from ..api import policy #..表示上一级目录,想再manage中使用policy中的方法就需要回到上一级glance目录往下找api包,从api导入policy └── db from . import models ├── __init__.py └── models.py
Ps:一定要在与glance同级的文件中测试 包里的模块如果想使用其他模块的内容只能使用相对路径,使用了相对路径就不能在包内直接执行了 可以用import导入内置或者第三方模块,但是要绝对避免使用import来导入自定义包的子模块, 应使用from…import…的绝对或者相对导入且包的相对导入只能用from的形式
单独导入
单独导入包名称时不会导入包中所有包含的所有子模块
在glance同级中的.py文件中
import glance
glance.api.policy()
'''
结果:AttributeError: module 'glance' has no attribute 'cmd'
'''
解决方法
# glance/__init__.py
from . import cmd
# glance/cmd/__init__.py
from . import manage
再次执行
# 在于glance同级的.py中
import glance
glance.api.policy()
__init__.py
文件
要在外界使用包的模块,需要再__init__.py
中指定对外界提供的模块列表
当前目录下导入
from . import send_message
all
__all__
可用于模块导入时限制 此时被导入模块若定义了__all__
属性,则只有all内指定的属性,方法,类可被导入
glance/
├── __init__.py from .api import *
from .cmd import *
from .db import *
├── api
│ ├── __init__.py __all__ = ['policy','versions']
│ ├── policy.py
│ └── versions.py
├── cmd __all__ = ['manage']
│ ├── __init__.py
│ └── manage.py
└── db __all__ = ['models']
├── __init__.py
└── models.py
import glance
policy.get()
发布模块及安装
创建setup.py
from distutils.core import setup
setup( name = “包名”,
version = “版本”,
description = “描述信息”,
long_description = “完整描述”,
author = “作者”,
url = “主页”,
py_modules = [
“包.模块”,..]
)构建模块
python3 setup.py build
生成发布压缩包
python3 setup.py sdist
安装模块
- 先解压
- 进行安装
tar -zxvf 压缩包
sudo python3 setup.py install
卸载模块
直接从安装目录下,把安装模块的目录删除就可以
cd /usr/loacl/lib/python3.x/dist-packages/
sudo rm -r 模块*
pip 安装第三方模块
sudo pip3 install 模块名
安装IPython
# MAC下
sudo pip install ipython
# Linux下
sudo apt install ipython
sudo apt install ipython3
0x10 文本操作
文件
:存储在某种长期储存设备上的一段数据
文件的作用
:将数据长期保存下来,在需要的时候使用
文件存储方式
:以二进制的方式保存在磁盘上的
文本文件:
- 可以使用文本编辑软件查看
- 本质上还是二进制文件
二进制文件:
- 保存的内容不是给人直接阅读的,而是提供给其他软件使用的
- 二进制文件不能使用文本编辑软件查看
文件的基本操作
基本步骤:
打开(
open()
)读/写
读
readlines() #读取所有行的内容 readline(n) #n:代表最长字节数,视频,图片,(rb,bytes按照字节读) read() #读取所有行的内容
写
write()
关闭(
close()
)
文件指针(光标)
文件指针标记
从哪个位置开始读取数据
第一次打开文件时,通常文件指针会指向文件的开始位置
当执行了read
方法后,文件指针会移动到读取内容的末尾
file.sekk()
移动文件读取指针到指定位置
sekk(offset,whence=0) ''' offset:开始的偏移量,也就是代表需要移动偏移的字节数 whence:给offset参数一个定义,表示要从哪个位置开始偏移,0代表从文件开头算起,1代表开始从当前位置开始算起,2代表从文件末尾开始算起,当有换行时,会被换行截断 seek()无返回值 '''
tell()
tell是获取文件指针位置
truncate()
用于截断文件并返回截断的字节长度
fileObject.truncate([size])
其他
readable()
是否可读可写
writable()
是否可写
删除文件与重命名
import os
# 删除文件
os.remove()
# 重命名
os.rename('xxxx.txr','rrrr')