面向对象(进阶)
目录:
面向对象高级语法部分:
- 静态方法、类方法、属性方法
- 类的特殊方法
- 反射
异常处理:
一、类成员(字段、方法和属性)
1、字段
字段包括:普通字段和静态字段,他们在定义和使用中有所区别,而最本质的区别是内存中保存的位置不同,
- 普通字段属于对象
- 静态字段属于类
class Province: # 静态字段 country = '中国' def __init__(self, name): # 普通字段 self.name = name # 直接访问普通字段 obj = Province('河北省') print(obj.name) # 直接访问静态字段 Province.country
*****************************************************************************
class foo:
name = 'Python2' #静态字段
def __init__(self,age):
#普通字段
self.age =age
self.gender ='男'
def fun(self):
print(self.name,self.age,self.gender)
#通过对象p直接调用普通字段
p = foo(12)
p.fun()
p.age
#类直接调用静态字段
foo.name
【普通字段需要通过对象来访问】【静态字段通过类访问】,在使用上可以看出普通字段和静态字段的归属是不同的。其在内容的存储方式类似如下图:
- 静态字段在内存中只保存一份
- 普通字段在每个对象中都要保存一份
应用场景: 通过类创建对象时,如果每个对象都具有相同的字段,那么就使用静态字段
二、方法
方法包括:普通方法、静态方法和类方法,三种方法在内存中都归属于类,区别在于调用方式不同。
- 普通方法:由对象调用;至少一个self参数;执行普通方法时,自动将调用该方法的对象赋值给self;
- 类方法:由类调用; 至少一个cls参数;执行类方法时,自动将调用该方法的类复制给cls;
- 静态方法:由类调用;无默认参数;
class foo: def __init__(self,name): self.name = name def A(self): #普通方法。至少一个参数self print(self.name,'普通方法') @staticmethod # 静态方法无参数 def B(): print('无参数, 静态方法') @classmethod def C(cls,name): #类方法,至少一个参数cls cls.name=name print(name,'类方法') #调用普通方法 p = foo('self参数') p.A() #self参数 普通方法 #调用静态方法 foo.B() #无参数, 静态方法 #调用类方法 foo.C('cls参数 ') #cls参数 类方法
相同点:对于所有的方法而言,均属于类(非对象)中,所以,在内存中也只保存一份。
不同点:方法调用者不同、调用方法时自动传入的参数不同。
三、属性(属性是普通方法的变种)
1、属性的基本使用
class foo: def __init__(self,name): self.n = name def fun1(self): print(self.n) #定义属性 @property def fun2(self): print(self.n) #方法调用 p = foo('Python2') p.fun1() """******************************""" #属性调用 foo('Python3').fun2
由属性的定义和调用要注意一下几点:
- 定义时,在普通方法的基础上添加 @property 装饰器;
- 定义时,属性仅有一个self参数
- 调用时,无需括号
方法:foo().fun1()
属性:foo().fun2
注意:属性存在意义:访问属性时可以制造出和访问字段完全相同的假象
属性由方法变种而来,如果Python中没有属性,方法完全可以代替其功能。
实例:
2、属性的两种定义方式:
- 装饰器 即:在方法上应用装饰器
- 静态字段 即:在类中定义值为property对象的静态字段
class foo: @property #装饰器调用 def fun1(self): return '装饰器方式添加属性' def fun2(self): return '静态字段方式' BAR = property(fun2) #静态字段调用 调用时改变了函数名为BAR #装饰器方式添加属性调用 p = foo() ret = p.fun1 print(ret) #静态字段方式调用 pf = foo() ret = pf.BAR print(ret)
property的构造方法中有个四个参数
- 第一个参数是方法名,调用
对象.属性
时自动触发执行方法 - 第二个参数是方法名,调用
对象.属性 = XXX
时自动触发执行方法 - 第三个参数是方法名,调用
del 对象.属性
时自动触发执行方法 - 第四个参数是字符串,调用
对象.属性.__doc__
,此参数是该属性的描述信息
class Foo: def get_fun(self): return 123 def set_fun(self,v): print('获取',v) def del_fun(self): print('del') per = property(fget=get_fun,fset=set_fun,fdel=del_fun,doc='adfasdfasdfasdf') # @property # def per(self): # return 123 obj = Foo() # ret = obj.per #获取 # print(ret) obj.per = 123456 #修改 # del obj.per #del删除
实例:
class Goods(object): def __init__(self): # 原价 self.original_price = 100 # 折扣 self.discount = 0.8 def get_price(self): # 实际价格 = 原价 * 折扣 new_price = self.original_price * self.discount return new_price def set_price(self, value): self.original_price = value def del_price(self, value): del self.original_price PRICE = property(get_price, set_price, del_price, '价格属性描述...') obj = Goods() obj.PRICE # 获取商品价格 obj.PRICE = 200 # 修改商品原价 del obj.PRICE # 删除商品原价 复制代码
四、类成员修饰符
类成员的两种形式:
- 公有成员,在任何地方都能访问
- 私有成员,只有在类的内部才能方法
私有成员定义:私有成员命名时,前两个字符是下划线。(特殊成员除外,例如:__init__、__call__、__dict__等)
# class foo: # def __init__(self,name,__age): # self.name = name # name 公有字段 # self.__age = __age # __age 私有字段 # def func(self): # print('name:%s'%self.name) # print('__age:%s'%self.__age) # # obj = foo('yang',18) # obj.func() # name:yang __age:18
静态字段
- 公有静态字段:类可以访问;类内部可以访问;派生类(子类)中可以访问
- 私有静态字段:仅类内部可以访问;
# class foo1(foo): # def fun(self): # print('name:%s' % self.name) # # print('__age:%s'%self.__age) #'foo1' object has no attribute '_foo1__age' # # obj = foo1('yang',90) # #foo.__age # obj.fun() # class foo: # name = '公有静态字段' # # def fun(self): # print(foo.name) # # class foo1(foo): # def dun(self): # print('name:%s'%foo.name) # # print(foo().name)
普通字段
- 公有普通字段:对象可以访问;类内部可以访问;派生类(子类)中可以访问
- 私有普通字段:仅类内部可以访问;
ps:如果想要强制访问私有字段,可以通过 【对象._类名__私有字段明 】访问(如:obj._C__foo),不建议强制访问私有成员。
# class foo: # __name = '私有静态字段' # # def fun(self): # print(foo.__name) # 类内调用私有字段 # # class foo1(foo): # def dun(self): # print('name:%s' % foo.__name) # 调用foo中的私有字段__name # # print(foo().fun()) # 私有静态字段 # # obj = foo1() # print(obj.dun()) # type object 'foo' has no attribute '_foo1__name'
五、类的特殊成员方法
1、__doc__
# __doc__ 表示类的注释信息 # class foo: # """ 这是foo的注释信息,用__doc__显示出来""" # def fun(self): # pass # # print(foo.__doc__) # 这是foo的注释信息,用__doc__显示出来 # __module__表示当前操作对象在哪个模块 # from 模块.py import foo # # obj = foo() # # print(obj.__module__) # 输出模块.py 打印初模块 # # print(obj.__class__) # 输出 模块.py.foo 打印初类
2、__module__和__class__
# __module__表示当前操作对象在哪个模块__class__输出对象调用的类 # from 模块.py import foo # # obj = foo() # # print(obj.__module__) # 输出模块.py 打印初模块 # # print(obj.__class__) # 输出 模块.py.foo 打印初类动执行foo中__init__方法
3、__init__构造方法
# __init__ 构造方法 构造方法,通过类创建对象时,自动触发执行。 # class foo: # def __init__(self,name): # self.name = name # # obj = foo('yang') #自动执行foo中__init__方法
4、__call__
对象后面加括号,触发执行。
注:构造方法的执行是由创建对象触发的,即:对象 = 类名() ;而对于 __call__ 方法的执行是由对象后加括号触发的,即:对象() 或者 类()()
# __call__ # class foo: # def __init__(self,name): # self.name = name # def __call__(self, *args, **kwargs): # print('name:%s'%self.name) # # obj = foo('yang') # obj() # 对象后面加() 直接执行 # foo('yang')() # 对象后面加() 直接执行
5、__dict__
# __dict__ # class foo: # def __init__(self,name,age): # self.name = name # self.age = age # def fun(self): # print('name:%s'%self.name) # print(foo.__dict__) # {'__module__': '__main__', '__init__': <function foo.__init__ # at 0x0000016F7C48BA60>, 'fun': <function foo.fun at 0x0000016F7C48B840>, # '__dict__': <attribute '__dict__' of 'foo' objects>, '__weakref__': <attribute '_ # _weakref__' of 'foo' objects>, '__doc__': None} # obj = foo('yang',22) # print(obj.__dict__) #{'name': 'yang', 'age': 22}
6、__str__
# __str__ 函数调用时,会自动执行__str__并输出返回值 # class foo: # def __str__(self): # return '自动返回__str__' # # obj = foo() # print(obj) #自动返回__str__
7、 __getitem__,__setitem__,__delitem__用于索引操作,如字典。分别表示,获取,设置,删除
# __getitem__,__setitem__,__delitem__用于索引操作,如字典。分别表示,获取,设置,删除 # class foo(object): # def __getitem__(self, value): # print('获取:',value) # def __setitem__(self, key, value): # print('设置:',key,value) # def __delitem__(self, key): # print('删除:',key) # # obj = foo() # lis = [1,24,45,98] # result = obj[lis[0]] #获取: 1 # obj[lis[1]] = 200 #设置: 24 200 # del obj[lis[2]] #删除: 45
8、__getslice__,__setslice__,delslice__ 分片操作
# __getslice__,__setslice__,delslice__ 分片操作 # class foo(object): # def __getslice__(self,i,j): # print('getslice:',i,j) # def __setslice__(self,i,j): # print('setslice:',i,j) # def __delslice__(self, i, j): # print('__delslice__', i, j) # obj = foo() # obj[-1:1] # 自动触发执行 __getslice__ # obj[0:1] = [11, 22, 33, 44] # 自动触发执行 __setslice__ # del obj[0:2] # 自动触发执行 __delslice__
9、__del__析构方法
析构方法,当对象在内存中被释放时,自动触发执行。
注:此方法一般无须定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给Python解释器来执行,所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的。
10. __iter__
用于迭代器,之所以列表、字典、元组可以进行for循环,是因为类型内部定义了 __iter__
11. __new__ 和 __metaclass__
12.super
super在子类中调用父类方法时用
class FooParent(object): def __init__(self): self.parent = 'I\'m the parent.' print('Parent') def bar(self, message): print(message, 'from Parent') class FooChild(FooParent): def __init__(self): super(FooChild, self).__init__() # 调用父类初始化方法 print('Child') def bar(self, message): super(FooChild, self).bar(message) # 调用父类bar方法 print('Child bar fuction') print(self.parent) if __name__ == '__main__': fooChild = FooChild() fooChild.bar('HelloWorld')
【推荐】FFA 2024大会视频回放:Apache Flink 的过去、现在及未来
【推荐】中国电信天翼云云端翼购节,2核2G云服务器一口价38元/年
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· [杂谈]如何选择:Session 还是 JWT?
· 硬盘空间消失之谜:Linux 服务器存储排查与优化全过程
· JavaScript是按顺序执行的吗?聊聊JavaScript中的变量提升
· [杂谈]后台日志该怎么打印
· Pascal 架构 GPU 在 vllm下的模型推理优化
· WinForm 通用权限框架,简单实用支持二次开发
· 如何为在线客服系统的 Web Api 后台主程序添加 Bootstrap 启动页面
· 硬盘空间消失之谜:Linux 服务器存储排查与优化全过程
· 面试官:DNS解析都整不明白,敢说你懂网络?我:嘤嘤嘤!
· 双语对照的 PDF 翻译工具「GitHub 热点速览」