15.面向对象和super

面向对象和super()

类(Class): 具有相同的属性和方法的对象的集合,即定义对象的模板

对象(Object): 类的实例化对象,有自己的属性和方法。

类方法: 使用装饰器@classmethod ,传递类的属性和方法(不能传实例的属性和方法)

类属性: 类中的变量。类变量在整个实例化的对象中是公用的

实例方法: 传递对象的属性和方法

实例属性: 使用 self 调用

封装:对外部世界隐藏对象的工作细节
多态:对不同类的对象使用同样的操作
继承:即一个派生类(derived class)继承基类(base class)的字段和方法
class Person():
    # #类属性
    name="小明"
    age=12   
    
    def __init__(self,id,email):
        实例属性
        self.id=id
        self.email=email
    
    实例方法
	def run(self):
        pass
    
xiaomi=Person(110,'110.com')    # 对象实例化
xiaomi.colour='red' 	        # 添加属性

print(Person.name)         # 类引用

类对象支持两种操作:属性引用和实例化

class Circle(object):  # 创建Circle类
   def __init__(self, r): # 初始化一个属性r(不要忘记self参数,他是类下面所有方法必须的参数)
       self.r = r  # 表示给我们将要创建的实例赋予属性r赋值
  • self 和对象 指向了同一个内存地址,对对象的引用
  • self代表类的实例,而非类

实例属性用于区分不同的实例
类属性是每个实例的共有属性。

保留关键字对实例的操作

getattr(obj, name[, default]) : 访问对象的属性。
hasattr(obj,name) : 检查是否存在一个属性。
setattr(obj,name,value) : 设置一个属性。如果属性不存在,会创建一个新属性。
delattr(obj, name) : 删除属性。

class Person():
    def __init__(self, name,home):
        self.name=name
        self.home=home
        print('我是Peson的__init__构造方法')

    def address(self):
        print("Name : ", self.name, ", address: ", self.home)



p1=Person("bei","BeiJing")
p1.address()
p1.age=18
p1.age=27

print(hasattr(p1, 'age')) # 如果存在 'age' 属性返回 True。
print(getattr(p1, 'age')) # 返回 'age' 属性的值

print(setattr(p1, 'age', 8))  # 添加属性 'age' 值为 8
print(delattr(p1, 'age'))     # 删除属性 'age'

# 执行结果
我是Peson的__init__构造方法
Name :  bei , address:  BeiJing
True
27
None
None
hasattr(emp1, 'age')    # 如果存在 'age' 属性返回 True。
getattr(emp1, 'age')    # 返回 'age' 属性的值
setattr(emp1, 'age', 8) # 添加属性 'age' 值为 8
delattr(emp1, 'age')    # 删除属性 'age'

内置属性类

__dict__ : 类的属性(包含一个字典,由类的数据属性组成)
__doc__ :类的文档字符串
__name__: 类名
__module__: 类定义所在的模块(类的全名是'main.className',如果类位于一个导入模块mymod中,那么className.module 等于 mymod)
__bases__ : 类的所有父类构成元素(包含了一个由所有父类组成的元组)

class Person():
    def __init__(self, name,home):
        self.name=name
        self.home=home
        print('我是Peson的__init__构造方法')

    def address(self):
        print("Name : ", self.name, ", address: ", self.home)

p1=Person("bei","BeiJing")

print ("Person.__doc__:", Person.__doc__)
print ("Person.__name__:", Person.__name__)
print ("Person.__module__:", Person.__module__)
print ("Person.__bases__:", Person.__bases__)
print ("Person.__dict__:", Person.__dict__)

# # ====================================================================
我是Peson的__init__构造方法
Person.__doc__: None
Person.__name__: Person
Person.__module__: __main__
Person.__bases__: (<class 'object'>,)
Person.__dict__: {'__module__': '__main__', '__init__': <function Person.__init__ at 0x000002619C7CD5E0>, 'address': <function Person.address at 0x000002619C8B3DC0>, '__dict__': <attribute '__dict__' of 'Person' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__doc__': None}

私有属性和私有方法

__foo__: 定义的是特殊方法,一般是系统定义名字 ,类似 __init__() 之类的。

_foo: 以单下划线开头的表示的是 protected 类型的变量,即保护类型只能允许其本身与子类进行访问,不能用于

from module import *

__foo: 双下划线的表示的是私有类型(private)的变量, 只能是允许这个类本身进行访问

私有属性包括 类属性和实例对象属性
私有方法包括 类方法和实例对象方法

双下划线
class Student():
	name="zhangsan"
	__age=12
	def __init__(self):
		__add="大红门"
        pass
    
	def __eat(self):
        pass
    pass

只能类的内部使用,不能在外部使用
1、不能被子类继承
2、不能实例调用

property

针对私有属性
property(get_day,set_day)

可以通过在类中定义__slots__变量来进行限定
 # 限定Person对象只能绑定_name, _age和_gender属性
    __slots__ = ('_name', '_age', '_gender')

super()

super() 是python 中调用父类(超类)的一种方法,在子类中可以通过super()方法来调用父类的方法。【超类: 是指 2层以上的继承关系,假如 C类继承B类,B类由继承A类,那么A类就是C类的超类】

通过super() 来调用父类的__init__ 构造方法

class Person():
    def __init__(self, name):
        print('我是Peson的__init__构造方法')


class Student(Person):
    def __init__(self, name):
        super(Student).__init__(name)
        print('我是Student的__init__构造方法')


class Teacher(Student):
    def __init__(self, name):
        super(Teacher).__init__(name)
        print('我是Teacher的__init__构造方法')


stu = Teacher(name="beike")
# 执行结果================================================================
我是Peson的__init__构造方法
我是Student的__init__构造方法
我是Teacher的__init__构造方法

通过supper() 来调用与子类同名的父类方法

class A:
    def __init__(self):
        self.n = 2

    def add(self, m):
        print('self is {0} @A.add'.format(self))
        self.n += m


class B(A):
    def __init__(self):
        super(B, self).__init__()
        self.n = 3

    def add(self, m):
        print('self is {0} @B.add'.format(self))
        super().add(m)
        self.n += 3


b = B()
b.add(2)
print(b.n)
# 执行结果=========================================================================
self is <__main__.B object at 0x106c49b38> @B.add
self is <__main__.B object at 0x106c49b38> @A.add
8

1、super().add(m) 确实调用了父类 A 的 add 方法。
2、super().add(m) 调用父类方法 def add(self, m) 时, 此时父类中 self 并不是父类的实例而是子类的实例, 所以 b.add(2) 之后的结果是 5 而不是 4 。

多继承

针对 菱形继承
钻石继承还有一个问题是,比如若B和C中的m方法也同时调用了A中的m方法时:

class A:
    def m(self):
        print("m of A called")

class B(A):
    def m(self):
        print("m of B called")
        A.m(self)

class C(A):
    def m(self):
        print("m of C called")
        A.m(self)

class D(B,C):
    def m(self):
        print("m of D called")
        B.m(self)
        C.m(self)
        
# 此时我们调用d.m,A.m则会执行两次。
d=D()
d.m()
# 执行结果 ===============================================================
m of D called
m of B called
m of A called
m of C called
m of A called

在多继承中,会涉及到一个MRO(继承父类方法时的顺序表) 的调用排序问题。即严格按照MRO 顺序执行super方法

其实上面两个问题的根源都跟MRO有关,MRO(Method Resolution Order)也叫方法解析顺序,主要用于在多重继承时判断调的属性来自于哪个类,其使用了一种叫做C3的算法,其基本思想时在避免同一类被调用多次的前提下,使用广度优先和从左到右的原则去寻找需要的属性和方法。

class A:
    def __init__(self):
        self.n = 2

    def add(self, m):
        print('self is {0} @A.add'.format(self))
        self.n += m

class B(A):
    def __init__(self):
        super(B, self).__init__()
        self.n = 3

    def add(self, m):
        print('self is {0} @B.add'.format(self))
        super().add(m)
        self.n += 3

class C(A):
    def __init__(self):
        super(C, self).__init__()
        self.n = 4

    def add(self, m):
        print('self is {0} @C.add'.format(self))
        super().add(m)
        self.n += 4

class D(B, C):
    def __init__(self):
        super(D, self).__init__()
        self.n = 5

    def add(self, m):
        print('self is {0} @D.add'.format(self))
        super().add(m)
        self.n += 5

d = D()
d.add(2)
print(d.n)

# # =================================================================================
self is <__main__.D object at 0x000001DC8F4F9700> @D.add
self is <__main__.D object at 0x000001DC8F4F9700> @B.add
self is <__main__.D object at 0x000001DC8F4F9700> @C.add
self is <__main__.D object at 0x000001DC8F4F9700> @A.add
19

新式类继承顺序:D->B->C->A, 广度优先,从左到右。

事实上类中的_mro_属性(method resolution order)中定义了继承顺序,我们可以打印出来验证一下:

print(D.mro())
# 执行结果  ==========================================================================
[<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]

参考资料

https://zhuanlan.zhihu.com/p/130404117

https://zhuanlan.zhihu.com/p/268136917

posted @ 2023-06-30 21:02  贝壳里的星海  阅读(6)  评论(0编辑  收藏  举报