python基础-类、对象、元类
类也是对象
面向过程:将复杂的流程化,进而简单化
面向对象:提升程序的解耦合程序,进而提高程序的可扩展性
类与对象
简单定义:
# 命名推荐采用大驼峰
class MyClass:
name = null
# 类的初始化方法,可以省略,self代表实例化的对象。返回值必须为None,或没有返回值
def __init__(self,param):
self.name = param
# 方法
def fun(self):
print(name)
# 查看类的属性和方法
print(MyClass.__dict__)
# 实例化
obj = MyClass('shuta')
# 使用对象的方法,在调用时会默认将对象本身作为第一个参数传递到方法中
obj.fun()
# 使用类调用方法,比如按照定义时的参数进行传递
MyClass(obj)
在类中除了属性和函数之外,也可以有能直接执行的语句,这些语句会在类定义时直接执行,如:print(),但不建议这么写。
隐藏属性
通过双下划线可以将变量或者方法定义为隐藏属性或方法,在python中隐藏属性是通过改变属性或函数的名字来实现的。
通过隐藏属性,可以隐藏内部的实现
class MyClass:
__name__ = 'shuta'
def __format__(self):
return '名字:'+self.__name__
def get_info(self):
return self.__format__
property
使用property可以用来装饰类中的方法,让访问方法和访问类一样
# 使用方式1:装饰方法为属性
class MyClass:
@property
def test():
return 'test'
obj = MyClass()
# 原来访问时,需要加括号,现在可以当作属性来访问
obj.test
# 使用方式2:修改属性的访问方式
class MyClass:
def __init__(self,name):
self.__name = name
def get_name(self):
print('获取了name')
return self.__name
def set_name(self,name):
print('修改了name')
self.__name = name
def del_name(self,name):
print('删除了name')
del self.__name
# 定义对name属性的操作,执行相应的方法,方法顺序,读-设置-删除
name = property(get_name,set_name,del_name)
obj = MyClass('shuta')
# 调用属性时,调用的为方法
obj.name
obj.name = 'shuta1'
del obj.name
# 方式2的另外一中写法
class MyClass:
def __init__(self,name):
self.__name = name
# 定义需要使用属性访问的方法
@property
def name(self):
print('获取了name')
return self.__name
# 定义设置时触发的方法
@name.setter
def set_name(self,name):
print('修改了name')
self.__name = name
# 定义删除时触发的方法
@name.deleter
def del_name(self,name):
print('删除了name')
del self.__name
obj = MyClass('shuta')
obj.name
obj.name = 'shuta1'
del obj.name
继承
python支持多继承.多继承的优缺点:
可以最大限度的重用代码
会让可读性变差,逻辑分析会比较麻烦,进而扩展性变差
在python3中所有的类默认都继承object 基类,
在python2中,同时存在经典类和新式类,经典类不继承object对象,如果有经典类继承了object那么他及子类都为新式类
菱形继承及非菱形继承
继承的几个类的基类为同一个则为菱形继承(除object为基类的情况),在菱形继承中,会从左到右的分支依次寻找属性或函数,但最后才会查找共同的头部类中的属性或函数(广度优先)。
非菱形继承从左到右的分子依次寻找(深度优先)
# 查看类的父类,只能看到一个
类.__base__
# 查看属性或者功能的查找顺序 ,只对新式类生效
类.mro()
mixins
mixins不是语法,而是规范.
通过mixins机制可以提升多继承下的可读性,对为类添加功能的类增加mixin后缀,与其他类进行区分
class A:
pass
class BMixin:
pass
class C(A,BMixin):
pass
子类派生方法中调用父类中的功能
方式一:在使用父类进行调用
class A:
def __init__(self):
pass
class B(A):
def __init__(self):
# 通过类嗲用,并传入当前对象
A.__init__(self)
方式二:使用super
使用super()会得到一个特殊的对象,该对象会参照当前类的MRO依次去寻找属性或方法
class A:
def __init__(self):
pass
class B(A):
def __init__(self):
# 通过类嗲用,并传入当前对象
super(A,self).__init__() # python2中的写法
super().__inti__() # python3可以这么写
mro 查找示例:
class A:
def test(self):
# 本案例中,此处的super()指向的为B类
super().test()
class B:
def test(self):
pass
class C(A,B):
pass
ob = C()
ob.test() # 查找顺序:A->B ,在A中遇到super(),根据C的MRO,A中执行super()后会去B中寻找
多态
多态指对象拥有相同的功能,但功能又不完全一样.
# 这是一个使用继承实现多态的方法
class A:
def get(self):
pass
class B(A):
def get(self):
pass
class C(A):
def get(self):
pass
def test(A)
return A.get()
b = B()
c = C()
test(b)
test(c)
鸭子类型
当某些类都有一个功能且又有差别时,python中约定对这些类写统一的方法进行调用,无需继承.即鸭子类型
通过鸭子类型,可以降低程序的耦合度
# 直接调用
b = B()
b.get()
tip:
python中支持使用抽象类来约束子类的功能,但更推崇鸭子类型
# 使用抽象 import abc # 设置为抽象类 class A(metaclass=abc.ABCMeta): @abc.abstractmethod # 限制子类需要实现的方法 def test(self): # 抽象方法中无需实现具体的功能 pass class B(A): # 子类必须实现,父类中的抽象方法,否则会抛出TypeError异常 def test(self): pass b = B() #
绑定方法-classmethos
包括两种:
绑定给对象的.(在使用对象调用时,自动传入对象)
绑定给类的(调用时,自动传入类)
经常用在通过读取参数生成对象的场景
class A:
@classmethods # 通过装饰器声明传进去的为类名
def get_obj(cls):
return cls()
a = A.get_obj()
非绑定方法(静态方法) -staticmethod
当一个函数不需要传入类也不需要传入对象时可以使用静态方法
class A:
@staticmethod
def test():
return 1
A.test()
反射
反射指在程序动态执行的过程中获取对象的信息
反射可以通过dir,__dict__实现
python提供了通过字符串操作属性值的方式
# 判断是否有某个属性
hasattr(对象,'属性名')
# 获取属性值
getattr(对象,'属性名')
# 设置属性值
setattr(对象,'属性名',值)
# 删除属性
delattr(对象,'属性名')
内置方法
打印时自动触发 str
删除对象时触发 del ,一般用于回收系统资源
class A: def __str__(self): return 'A' def __del__(self): pass a = A() print(a) #调用__str__ del a # 调用del
tip:
在程序运行结束,回收内存的时候也会先执行__del__
元类
其他类都是通过元类创建出来的。对象是类的实例化,类是对象的实例化。
创建类时,即为调用type的实例化过程__call__():
执行要
__new__()
创建出类的空对象,如果要创建中的类不存在__new__()
,则会寻找到type中(因为类为元类的实现)执行
__init()__
对数据进行初始化,返回创建好的类
tip:创建类时的
__call__
与对象调用时的__call__
的区别
type与object的关系
object是type类的实现
type继承object
在python中的源码中,会预先初始化type与object的内存,此时都处于不可使用的状态,然后在初始化时,建立他们之间的关系,此时就可以调用了
class机制的四个步骤
元类中的 new call init 及对象中的分析
类和对象下的属性查找步骤
如果调用普通对象的属性,则查找顺序为:
对象本身 -> 父类 -> object类 (不会去查找type类,因为对象的基类为object,非type)
如果直接调用类的属性,则查找顺序为:
类本身 -> 自定义原类 -> type