Python 面向对象
本章内容简介:
1. 类的成员:
1) 字段 : 普通字段、静态字段;
2) 方法 : 普通方法、静态方法、类方法;
3) 属性 : 普通属性(获取、设置、删除);
2. 成员修饰符:
1)公有的 任何 都可以调用;
2)私有的 仅类自己内部可以调用;可以用一个另类的方法调用,就是 obj._类名__私有(字段等),好吧,知道能这么用就行了,最好忘了它。
3. 类的特殊成员:
__init__ 构造方法;
__dic__ 注释文档查看,函数下加引号写的第一位置,会被此成员读取;
__call__ 变量加括号执行call方法;
__str__ 将内存地址的内容,读取成str内容;
__setitem__
......
4. 面向对象的其它内容:
-- isinstance 判断一个对象是否属于某个类;
-- issubclass 判断一个类是否属于某个类的子类;
-- 执行父类构造方法
-- 应用:
自定义类型,对字典进行补充,有序字典,源码的扩展;
5. 设计模式之单例模式;
6. 异常处理:
try === except === = finally
一. 类的成员
- 字段
1. 普通字段,实例:
class Foo: def __init__(self,name): self.name = name
代码解析:
name就是普通字段,当Foo函数,被实例化时,如:obj = Foo('hailong') ,对象obj 就使用了name普通字段,普通字典默认被对象使用,所以普通字段是保存在对象里的;
2. 静态字段,实例:
class Province: country = "中国" # 静态字段,保存在类里,节省内存空间; def __init__(self,name): self.name = name country = Province.country tj = Province("天津") print(tj.country ,' ',country)
代码解析:
country是静态字段,如上实例所示,静态字段,类可以调用,对象也可以调用,其它语言里是不允许的,之所以对象可以调用静态字段,是因为对象里的类对象指针(对象去数据时,是先从自己本身找,找不到,通过类对象指针到类里找)通过这种方式来访问的;通常情况下,我们要使用类来调用静态字段;
总结:
普通字段,用对象来访问; 静态字段,使用类来访问,实在没办法时才用对象访问;
当执行程序时,如果没创建对象时,普通字段不会加载到内存,但是静态字段默认会加载到内存;
- 方法
1. 普通方法,实例:
class Province: country = "中国" def __init__(self,name): self.name = name def show(self): # 方法属于类,是由对象调用的 print(self.name) tj = Province("天津") tj.show()
代码解析:
方法属于类,普通方法存在类里,是由对象调用的;
2. 静态方法,实例:
class Province: country = "中国" # 静态字段,保存在类里,节省内存空间; def __init__(self,name): self.name = name @staticmethod # 静态方法,是由类调用的 def func(arg1,arg2): print(arg1,arg2) Province.func(123,'qcc')
代码解析:
静态方法是由类调用的,就是方法去掉self参数后,是用装饰器staticmethod,传参自定义,不需要创建对象,就可以执行的方法。用于一些不通用的方法,这样节省内存空间;
相当于Python的函数;
3. 类方法,实例:
class Province: country = "中国" # 静态字段,保存在类里,节省内存空间; def __init__(self,name): self.name = name @classmethod def f1(cls): print(cls) Province.f1()
代码解析:
类方法是由类调用的,它有一个默认的形式参数cls,cls相当于类名;
- 属性
1. 普通属性,实例:
class Pager: def __init__(self,count_pager): self.count_pager = count_pager @property # 普通属性 def all_pager(self): a1,a2=divmod(self.count_pager,10) if a2 == 0: return a1 return a1+1 p = Pager(101) print(p.all_pager) # 执行普通属性,不用加括号了
代码解析:
普通属性,具有方法的访问写形式,具有字段的访问形式。 相当于方法去掉括号,由对象调用;
2. 属性操作(设置)实例:
class Pager: def __init__(self,count_pager): self.count_pager = count_pager @property def all_pager(self): a1,a2=divmod(self.count_pager,10) if a2 == 0: return a1 return a1+1 @all_pager.setter def all_pager(self,value): print(value) p = Pager(101) p.count_pager = 211 # 设置属性,对属性的方法进行setter; print(p.count_pager)
代码解析:
对象生成后,count_pager 根据类参数定义一个值,当想要使用一个新值时,需要使用到设置属性,给字段count_pager重新赋值,使用普通属性方法setter装饰器,重新给字段赋值;
3. 属性操作(删除)实例:
class Pager:
def __init__(self,count_pager):
self.count_pager = count_pager
@property
def all_pager(self):
a1,a2=divmod(self.count_pager,10)
if a2 == 0:
return a1
return a1+1
@all_pager.setter
def all_pager(self,value):
print(value)
@all_pager.deleter
def all_pager(self):
print("del all_pager")
p = Pager(101)
p.count_pager = 211 #设置方法,会调用all_pager.setter下的方法;
print(p.count_pager)
del p.all_pager # del 是个语法,会调用all_pager.deleter下的方法;
property的构造方法中有个四个参数
- 第一个参数是方法名,调用
对象.属性时自动触发执行方法 - 第二个参数是方法名,调用
对象.属性 = XXX时自动触发执行方法 - 第三个参数是方法名,调用
del 对象.属性时自动触发执行方法 - 第四个参数是字符串,调用
对象.属性.__doc__,此参数是该属性的描述信息
属性的另一种写法
class Pager:
def __init__(self,name):
self.name = name
def f1(self):
return 123
def f2(self,value):
print(value)
def f3(self):
print('del f1') # 还没弄明白,具体怎么用
foo = property(fget=f1,fset=f2,fdel=f3)
obj = Pager('hailong')
res = obj.foo
print(res)
obj.foo = 456
del obj.foo
代码解析:
不用加装饰器了,直接给静态字段赋值,使用property,来完成属性的功能;
由属性的定义和调用要注意一下几点:
- 定义时,在普通方法的基础上添加 @property 装饰器;
- 定义时,属性仅有一个self参数
- 调用时,无需括号
方法:foo_obj.func()
属性:foo_obj.prop
注意:属性存在意义是:访问属性时可以制造出和访问字段完全相同的假象
属性由方法变种而来,如果Python中没有属性,方法完全可以代替其功能。
二. 成员修饰符
1. 公有的,实例:
class Province(object):
def __init__(self,name):
self.name = name
hb = Province("河北")
print(hb.name) #name是公有的,任何对象都可以调用;
代码解析:
这个例子里,name就是公共的,任何基于Province创建的对象都能使用;
2. 私有修饰符,实例(一):
class Province(object):
def __init__(self,name):
self.__name = name
def f1(self):
print(self.__name)
hb = Province("河北")
hb.f1()
# print(hb.__name) # 报错 AttributeError: 'Province' object has no attribute '__name'
代码解析:
当把公有的,加上双下划线后,就变成私有的了,这时外部不能调用,只能是类里的方法可以调用;只有自己访问可以,不能被继承;
实例(二):
class Province(object):
__paw = 123
def __init__(self,name):
self.__name = name
def f1(self):
print(Province.__paw)
@staticmethod
def f2():
print(Province.__paw)
hb = Province("河北")
hb.f1() # 通过变量调用使用了私有字段的方法;
Province.f2() #直接用类调用,静态方法里的私有字段;
hb.f2() # 也可以成功,但是不建议这样使用;
Province.f1() # 不可以在外部直接使用类里的私有字段,报错 TypeError: f1() missing 1 required positional argument: 'self'
三. 特殊成员
上文介绍了Python的类成员以及成员修饰符,从而了解到类中有字段、方法和属性三大类成员,并且成员名前如果有两个下划线,则表示该成员是私有成员,私有成员只能由类内部调用。无论人或事物往往都有不按套路出牌的情况,Python的类成员也是如此,存在着一些具有特殊含义的成员,详情如下:
1. del、call 和 str方法:
class Foo(object):
instance = None
def __init__(self,name,age):
self.name=name
self.age = age
# 析构方法,系统垃圾回收之前执行这个方法;
def __del__(self):
pass
#
def __call__(self, *args, **kwargs):
print("call")
#
def __str__(self):
return "%s - %s" % (self.name,self.age)
obj = Foo("hailong",23)
obj1 = Foo("eric",23)
print(obj) # 执行时,调用了str方法
res = str(obj1) # 执行时,调用了str方法
obj() # 变量加括号,执行call方法;
Foo("hailong",23)() # 类执行,加括号执行call方法;
print(obj1)
2. add 、 dict 方法:
class Foo:
def __init__(self,name,age):
self.name = name
self.age = age
def __add__(self, other):
temp = "%s - %d" % (self.name,other.age)
return temp
obj1= Foo("hailong",23)
obj2= Foo('eric',19)
res = obj1 + obj2 # 执行add方法;
print(res)
print(obj1.__dict__) # 返回obj1封装的内容,字典格式输出;
如果执行 类.__dict__ 会打印出类里封装的所有的成员,一般用于对象的封装查看;
3. getitem、setitem和delitem方法:
class Foo:
def __init__(self,name,age):
self.name = name
self.age = age
def __getitem__(self, item):
return 123
def __setitem__(self, key, value):
print(key,value)
def __delitem__(self, key):
print("del %s" % key)
obj1= Foo("hailong",23)
res = obj1['abc'] # 执行getitem方法
print(res,"----")
obj1['k1'] = 122 # 执行了setitem方法 ,执行结果是k1 122
print(obj1.__dict__) # obj1没有任何改变;
del obj1['k1'] # 执行delitem方法,
对象后加括号执行call方法,加上中括号执行 getitem方法。。。
4. getitem 切片方法:
class Foo:
def __init__(self,name,age):
self.name = name
self.age = age
def __getitem__(self, item):
print(item.start)
print(item.stop)
print(item.step)
return 123
def __setitem__(self, key, value):
print(key,value)
obj1= Foo("hailong",23)
# res = obj1['abc'] # 字符串方法没有切片start等位置属性;
res1 = obj1[0:4:2]
5. iter 迭代 方法:
用于迭代器,之所以列表、字典、元组可以进行for循环,是因为类型内部定义了 __iter__
class Foo:
def __init__(self,name,age):
self.name = name
self.age = age
def __iter__(self): # iter方法
return iter([11,22,33,44]) # 可迭代类型;
obj = Foo("hailong",23)
for item in obj:
print(item)
for循环,例子二:
class Foo:
def __init__(self,name,age):
self.name = name
self.age = age
def __iter__(self):
yield 1
yield 2
obj = Foo("hailong",23)
for item in obj:
print(item)
6. __module__ 和 __class__
__module__ 表示当前操作的对象在那个模块
__class__ 表示当前操作的对象的类是什么
四. 面向对象的其它内容
1. isinstance 和 issubclass 内置变量;
class Foo:
pass
class Func(Foo):
pass
obj = Foo()
res = isinstance(obj,Foo) # obj 是 基于Foo类创建的,返回True;
print(res)
ret = issubclass(Func,Foo) # Func 类是 Foo类的一个子类,返回True;
print(ret)
2. 执行父类构造方法;
如何执行,看实例:
class Foo:
def f1(self):
print("Foo.f1")
class Func(Foo):
def f1(self):
super(Func,self).f1()
print('Func.f1')
obj = Func()
res = obj.f1() # obj 执行的了自己的f1方法也执行了父类里的f1方法,因为有super;
这个方法应用于:当想要使用其它程序的功能或框架里的方法时,继承框架或功能的类方法,使用super就能使用框架的功能了;
3. 有序字典:
class Mydict(dict):
def __init__(self):
self.li = []
super(Mydict,self).__init__()
def __setitem__(self, key, value):
self.li.append(key)
super(Mydict,self).__setitem__(key,value)
def __str__(self):
temp_li = []
for key in self.li:
value = self.get(key)
temp_li.append("'%s':%s" %(key,value))
temp_str = "{" + ",".join(temp_li) + "}"
return temp_str
obj = Mydict()
obj['k1'] = 123
obj['k2'] = 456
print(obj)
代码解析:
继承字典的类,将正常生成字典的key,添加到一个空列表,通过自定义类型对字典的生成过程重新定义,将无序存储的字典,写成一个列表,根据字符串拼接的方法,读取成有序的字典;
五. 设计模式之单例模式
用来创建单个实例,什么时候用? 不管获取多少个实例,他们的内存地址是一样的;
class Foo:
instance = None
def __init__(self,name):
self.name = name
@classmethod
def get_instance(cls):
if cls.instance:
return cls.instance
else:
obj = cls('hailong')
cls.instance = obj
return obj
obj1 = Foo.get_instance()
obj2 = Foo.get_instance() # 不管获取多少个实例,他们的内存地址是一样的;
print(obj1,obj2) # 两个对象内存地址相同;
六. 异常处理
try --->except try内容如果不报错,永远不会执行except的内容;
1、异常基础
在编程过程中为了增加友好性,在程序出现bug时一般不会将错误信息显示给用户,而是现实一个提示的页面,通俗来说就是不让用户看见大黄页!!!
try: pass except Exception,ex: pass
需求:将用户输入的两个数字相加
while True:
num1 = input('input num1:')
num2 = input('input num2:')
try:
num1 = int(num1)
num2 = int(num2)
result = num1 + num2
try:
print(result)
except NameError as A:
print(A)
except Exception as E:
print(E)
2、异常种类
python中的异常种类非常多,每个异常专门用于处理某一项异常!!!
1) 常用异常:
AttributeError 试图访问一个对象没有的树形,比如foo.x,但是foo没有属性x IOError 输入/输出异常;基本上是无法打开文件 ImportError 无法引入模块或包;基本上是路径问题或名称错误 IndentationError 语法错误(的子类) ;代码没有正确对齐 IndexError 下标索引超出序列边界,比如当x只有三个元素,却试图访问x[5] KeyError 试图访问字典里不存在的键 KeyboardInterrupt Ctrl+C被按下 NameError 使用一个还未被赋予对象的变量 SyntaxError Python代码非法,代码不能编译(个人认为这是语法错误,写错了) TypeError 传入对象类型与要求的不符合 UnboundLocalError 试图访问一个还未被设置的局部变量,基本上是由于另有一个同名的全局变量, 导致你以为正在访问它 ValueError 传入一个调用者不期望的值,即使值的类型是正确的
对于特殊处理或提醒的异常需要先定义,最后定义Exception来确保程序正常运行。
2)另一种结构的异常:
try:
# 主代码块
pass
except KeyError as ex:
# 异常时,执行该块
pass
else:
# 主代码块执行完,执行该块
pass
finally:
# 无论异常与否,最终执行该块
pass
3) 主动抛出异常:
try:
raise Exception('错误了。。。')
except Exception as ex:
print(ex)
4) 自定义异常:
class WupeiqiException(Exception):
def __init__(self, msg):
self.message = msg
def __str__(self):
return self.message
try:
raise WupeiqiException('我的异常')
except WupeiqiException as e:
print(e)
5)断言
# assert 条件 assert 1 == 1 assert 1 == 2
条件成立执行,不成立就不执行;
________________________________________________________END____________________________________________________