文章目录
- 类继承方法重复:
- 多继承调用顺序问题
- 类属性
- 类方法
- 实例属性
- 实例方法
- 静态方法
- 类对象和实例对象可调用的范围
- property属性
- 应用场景
- property方法四个参数
- 有关访问限制
- 一些魔法属性
- doc
- module
- class
- init
- call
- dict
- str
- getitem、setitem、delitem
- getslice、setslice、delslice
- new
- 接口
类继承方法重复:
- ClassExample|ClassName.__dict__ 以字典形式返回类或类实例的属性
- ClassExample.__class__ 返回实例的创建对象类、
- ClassName.__mro__ 存在继承时,返回该类执行时super方法调用类对象的顺序
如果子类中也有__init__方法,则会覆盖,需要在子类中的__init__方法引入父类的__inti__方法。
class Height():
def __init__(self,height):
self.heigth = height
print('Height has executed')
class Weight():
def __init__(self,weight):
self.weight = weight
print('Weight has executed')
class Person(Height,Weight):
def __init__(self,name):
self.name = name
print('Person has executed!')
m = Person('小明')
#继承的父类:[Height,Weight]的__init__方法没有调用,被子类覆盖了。
# out:Person has executed!
class Person(Height,Weight):
def __init__(self,name,height,weight):
self.name = name
Height.__init__(self,height)
Weight.__init__(self,weight)
print('Person has executed!')
m = Person('小明',178,'30kg')
print(m.__class__)
print(m.__dict__)
print(Person.__mro__)
# 在子类初始化方法__init__中引入父类该方法
#out: Height has executed
#out: Weight has executed
#out: Person has executed!
#out: <class '__main__.Person'>
#out: {'name': '小明', 'heigth': 178, 'weight': '30kg'}
#out: (<class '__main__.Person'>, <class '__main__.Height'>, <class '__main__.Weight'>, <class 'object'>)
多继承调用顺序问题
如果类C,类B都继承了类A,而类D继承了类B和类C,这样通过父类名字引入父类的属性,类B和类C都会调用类A,这样就会浪费许多资源,使用super方法引入继承类的属性,就可以有效避免这点。
super方法:使用了c3算法,保证每一个类只调用一次,计算的类调用顺序
class Parent():
def __init__(self,name,*args,**kwargs): #为避免多继承报错,使用不定长参数,接受参数
self.name = name
print('Parent has executed!')
class Son1(Parent):
def __init__(self,name,age,*args,**kwargs):
self.age = age
super().__init__(name,*args,**kwargs)
print('Son1 has executed!')
class Son2(Parent):
def __init__(self,name,weight,*args,**kwargs):
self.weight = weight
super().__init__(name, *args, **kwargs)
print('Son2 has executed!')
class GrandSon(Son1,Son2):
def __init__(self,name,age,weight):
print('GrandSon has executde!')
super().__init__(name,age,weight)
if __name__ == '__main__':
m = GrandSon('小明',25,178)
print(GrandSon.__mro__)
#out: GrandSon has executde!
#out: Parent has executed!
#out: Son2 has executed!
#out: Son1 has executed!
#Parent类只调用了一次。
#out: (<class '__main__.GrandSon'>, <class '__main__.Son1'>, <class '__main__.Son2'>, <class '__main__.Parent'>, <class 'object'>)
super方法里面也可以传入类名,我们将上面的类Grandson改写下,如下输入super传入类名‘Son2’,便从__mro__元组的Son2的下一个类开始继承调用:
class GrandSon(Son1,Son2):
def __init__(self,name,age,weight):
print('GrandSon has executde!')
super(Son2,self).__init__(name,age,weight)
if __name__ == '__main__':
m = GrandSon('小明',25,178)
print(GrandSon.__mro__)
print(m.__dict__)
#out: GrandSon has executde!
#out: Parent has executed!
#out: (<class '__main__.GrandSon'>, <class '__main__.Son1'>, <class '__main__.Son2'>, <class '__main__.Parent'>, <class 'object'>)
# 只调用了Son2后面的类Parent,所以只有name属性
#out: {'name': '小明'}
类属性
直接在类里面定义变量,即为类的属性,如下示例的cls_var
类方法
使用@classmethod
装饰器创建,至少有一个cls参数(也可以是别的名字)
实例属性
__init__(self)
魔法方法中传入的变量属性,如下示例的self.name
实例方法
定义实例方法,至少有一个self参数(也可以是别的名字)
比如如下示例的cls_exp_fun(self)
静态方法
其实就是一个普通函数,和类对象实例对象无关,可以使用@staticmethod
装饰器创建。其作用表现为该方法只针对该类调用。(如果类A要调用函数名为test的函数,类B也要调用函数名为test的函数,而这个两个函数的方法是不一样的,如果test只是一个独立的函数,写两次则会覆盖,都指向为一个test函数,将test创建为类的静态方法就可以避免这点)
类对象和实例对象可调用的范围
- 类对象可以调用类属性、类方法及静态方法;
- 类的实例都可以调用,包括类属性,类方法,实例属性,实例方法,静态方法,实例方法可有其特有的属性,其他与类对象共用。
class Test():
cls_var = '类属性:cls_var'
def __init__(self,name):
self.name = '实例变量name:{}'.format(name)
def cls_exp_fun(self):
'''定义实例方法,至少有一个self参数(也可以是别的名字)'''
print('这是一个实例方法:cls_exp_cun')
@classmethod
def cls_fun(cls,name):
'''定义类方法,至少有一个cls参数(也可以是别的名字)'''
print('这是一个类方法:cls_fun')
cls.name = name
@staticmethod
def static_fun():
'''定义静态方法,无默认参数'''
print('这是一个静态方法:static_fun')
if __name__ == '__main__':
m = Test('小叔本')
# 调用实例方法
m.cls_exp_fun()
# 实例调用类方法,该方法会创建类name属性
m.cls_fun('实例调用类方法')
# 调用类方法,类属性name被修改
Test.cls_fun('类属性:name')
# 实例调用静态方法
m.static_fun()
# 类也可以调用静态方法
Test.static_fun()
# 实例的'__class__'属性执行创建实例类对象,可以通过该指向修改类的一些属性,比如下面修改类的name属性。
m.__class__.name = '修改类的属性name'
# 我们再查看下类与实例的属性
print(m.__dict__,'\n',Test.__dict__)
print输出:
这是一个实例方法:cls_exp_cun
这是一个类方法:cls_fun
这是一个类方法:cls_fun
这是一个静态方法:static_fun
这是一个静态方法:static_fun
{‘name’: ‘实例变量name:小叔本’}
{‘module’: ‘main’, ‘cls_var’: ‘类属性:cls_var’, ‘init’: <function Test.init at 0x00000251B4F1A048>, ‘cls_exp_fun’: <function Test.cls_exp_fun at 0x00000251B4F1A0D0>, ‘cls_fun’: <classmethod object at 0x00000251ADF1B860>, ‘static_fun’: <staticmethod object at 0x00000251ADF1B7F0>, ‘dict’: <attribute ‘dict’ of ‘Test’ objects>, ‘weakref’: <attribute ‘weakref’ of ‘Test’ objects>, ‘doc’: None, ‘name’: ‘修改类的属性name’}
实力调用类属性:cls_var
property属性
应用场景
- 实际上我们也可以使用函数返回,使用property会使得代码更为清晰,另一方面,使用函数时,我们往往需要了解函数相关参数,直接使用property可以避免这点。
- Python的property属性的功能是:property属性内部进行一系列的逻辑计算,最终将计算结果返回。
- property属性的两种方式:
- 装饰器:在方法上应用装饰器
- 类属性:在类中定义值为property对象的类属性
class Sale():
'''
python3中默认继承object类,py3具有@xxx.setter,@xxx.deleter方法,这两种装饰器在py2中使用不了。
'''
def __init__(self):
self.__numb = 888
@property
def num(self):
print('@property')
return self.__numb
@num.setter
def num(self,value): #需要传入value参数
print('@price.setter')
self.__numb = value
@num.deleter
def num(self):
print('@price.deleter')
del self.__numb
if __name__ == '__main__':
m = Sale()
m.num # 自动调用property装饰的num函数
print(m.num)
m.num = 999 # 自动调用num.setter装饰的num函数
print(m.num)
del m.num # 自动调用num.deleter装饰的num函数
#print(m.num)
m._Sale__numb #可以通过这种方式访问类的私有属性
out:
@property
@property
888
@price.setter
@property
999
@price.deleter
property方法四个参数
- 第一个参数是方法名,调用对象.属性时自动触发执行方法
- 第二个参数是方法名,调用对象.属性=xxx时自动触发执行方法
- 第三个是方法名,调用del 对象.属性时自动触发执行方法
- 第四个参数是字符串,调用对象.属性.__doc__,此参数是改属性的描述。
class Sale():
'''
python3中默认继承object类,py3具有@xxx.setter,@xxx.deleter方法
'''
def __init__(self):
self.numb = 888
def num(self):
print('@property')
return self.numb
def set_num(self,value): #需要传入value参数
print('@price.setter')
self.numb = value
def del_num(self):
print('@price.deleter')
del self.numb
num_var = property(num,set_num,del_num,'description...')
if __name__ == '__main__':
m = Sale()
m.num_var # 自动调用num函数
m.num_var = 20 # 自动调用set_num函数
del m.num_var # 自动调用del_num.deleter
m.num_var.__doc__
有关访问限制
python并没有对属性和方法的访问权限进行限制。为了保证类内部的某些属性或方法不被外部所访问,可以在属性或方法前面添加单下划线、双下划线,或首尾双下划线
- 首尾双下划线表示定义特殊的方法,一般是系统定义名字,如__init__()。
- 以单下划线开头的表示protected(保护)类型的成员,只允许类本身和子类进行访问,但不能使用“from module import *”语句导入。
- 双下划线表示private(私有)类型成员,只允许定义该方法的类本身进行访问,而且也不能通过类的实例进行访问,但是可以通过“类的实例名._类名__xxx”进行访问。
一些魔法属性
doc
__doc__:类的描述信息
module
__module__:当前操作的对象在哪个模块
class
__class__:当前操作的对象的类是什么
class Cls():
'''类的描述信息'''
def func(self):
pass
print(Cls.__doc__)
print(Cls.__module__)
m = Cls()
print(m.__class__)
init
__init__:初始化方法,通过类创建对象,自动触发执行
call
__call__:类的描述信息,对象后面加括号,触发执行。
class Cls():
def __init__(self):
pass
def __call__(self,*args,**kwargs):
print('__call__')
m = Cls()
m() #触发执行__call__函数
dict
__dict__:类或对象中的所有属性,类的实例属性属于对象,类中的类属性和方法属于类。
str
__str__:如果定义了一个__str__方法,在print时,默认输出该方法的返回值。
class Cls():
def __str__(slef):
return '__str__'
m = Cls()
print(m)
getitem、setitem、delitem
__getitem__、__setitem__、__delitem__
用于索引操作,如字典。以上分别表示获取、设置、删除数据。
class Cls():
def __init__(slef):
self.dic = {'name':'张三'}
def __getitem__(self,key):
return self.dic[key]
def __setitem__(self,key,value):
self.dic[key] = value
def __delitem__(self,key):
del self.dic[key]
m = Cls()
result = m['name']
m['age'] = 25
del m['age']
getslice、setslice、delslice
__getslice__、__setslice__、__delslice__:该三个方法用于分片操作,如列表
class Cls():
def __init__(slef):
self.ls = [1,3,5,9,10]
def __getslice__(self,start,end):
return self.ls[start:end]
def __setslice__(self,start,end,sequence):
self.ls[start:end] = sequence
def __delslice__(self,start,end):
del self.ls[start:end]
m = Cls()
result = m[-1:1] #自动触发__getslice__
m[0:2] = [33,44,55,66] #自动触发__setslice__,复制列表长度大于切片长度,实际赋值m[0:4]
del m[0:1] #自动触发__delslice__
new
__new__:为类申请一块内存空间
在一个类定义中,如果没有重写(覆盖)__new__方法,类实例化会默认执行父类的__new__的方法,如果父类中没有该方法,则追溯执行类的基类object的__new__方法;
__init__方法是在类__new__方法返回内存空间后才执行。如果__new__方法在类定义时被重写而没有返回,__init__方法不会被调用,实例的对象返回是None。
class A(object):
def __init__(self,value):
self.num = value
print('---->执行__init__方法')
def __new__(cls,*args,**kwargs):
'''申请返回一块内存地址'''
print('---->执行__new__方法')
position = super(cls,A).__new__(cls)
return position # 如果没有return返回,__init__方法不会被调用
if __name__ == '__main__':
m = A(10)
print(m) # 如果重写的__new__方法没有return,m返回是None
print('num:{}'.format(m.num))
'''
执行顺序:
__new__申请一块内存地址 --> __init__方法承接__new__返回的内存地址执行 --> 返回实例对象m
'''
代码执行打印如下:
接口
若干抽象方法的集合,类似于scala的抽象类;
作用:限制实现接口的类必须按照接口给定的调用方式实现这些方法;对高层模块隐藏了类的内部实现;
from abc import ABCMeta,abstractclassmethod
class EatSomething(metaclass=ABCMeta):
# 使用了该装饰,其他类继承该方法,重写调用方式必须与该方法一致(函数参数);
@abstractclassmethod
def eat(self,item):
pass
class Bob(EatSomething):
# 重写,调用方法与抽象方法一致
def eat(self,item):
print(f"Bob吃了{item}")
class Alice(EatSomething):
def eat(self,item):
print(f"Alice吃了{item}")
if __name__ == "__main__":
p = Bob()
p.eat("apple")