面向对象的三大属性

 

1,面向对象中的继承表示的是类与类之间的关系(什么是什么的关系),在python3中,所有的类都会默认继承object类,继承了object类的所有类都是新式类,如果一个类没有继承任何父类,那么__bases__属性就会显示<class 'object'>。

2,继承可以分为单继承和多继承。

# 单继承
class Parent:pass
class Son(Parent):pass   # 继承关系
# Son继承了Parent
print(Son.__bases__)  # 内置的属性
print(Parent.__bases__)  # 内置的属性


# 多继承
class Parent1:pass
class Parent2(Parent1):pass
class Parent3:pass
class Son(Parent2,Parent3):pass   # 继承关系
print(Parent2.__bases__)
print(Son.__bases__)
View Code

3,对象使用名字的顺序: 先找对象自己内存空间中的,再找对象自己类中的,再找父类中的。

class Animal:
    role = 'Animal'
    def __init__(self,name,hp,ad):
        self.name = name     # 对象属性 属性
        self.hp = hp         #血量
        self.ad = ad         #攻击力
    def eat(self):
        print('%s吃药回血了'%self.name)

class Person(Animal):
    r = 'Person'
    def attack(self,dog):   # 派生方法
        print("%s攻击了%s"%(self.name,dog.name))

    def eat2(self):
        print('执行了Person类的eat方法')
        self.money = 100
        self.money -= 10
        self.hp += 10

class Dog(Animal):
    def bite(self,person):  # 派生方法
        print("%s咬了%s" % (self.name, person.name))

# 继承中的init
alex = Person('alex',10,5)
print(Person.role)
print(alex.__dict__)
dog = Dog('teddy',100,20)
print(dog.__dict__)

# 继承中的派生方法
alex = Person('alex',10,5)
dog = Dog('teddy',100,20)
alex.attack(dog)
dog.bite(alex)

# 继承父类的方法:自己没有同名方法
alex = Person('alex',10,5)
dog = Dog('teddy',100,20)
alex.eat2()
alex.eat()
dog.eat()
View Code
# class Animal:
#     def __init__(self,name,sex,kind):
#         self.name = name
#         self.sex = sex
#         self.kind = kind
#     def eat(self):
#         print('%s is eating'%self.name)
#
#     def drink(self):
#         print('%s is drinking'%self.name)
#
# class Cat(Animal):
#     def climb(self):
#         print('%s is climbing'%self.name)
#
# class Dog(Animal):
#     def watch_door(self):
#         print('%s is watching door'%self.name)

# 1.确认自己没有init方法
# 2.看看有没有父类
# 3.发现父类Animal有init
# 4.看着父类的init方法来传参数
#tom = Cat('tom','公','招财猫')   # 实例化对象
# tom.eat = '猫粮'
# print(Cat.__dict__)   # Cat.__dict__ Cat类的命名空间中的所有名字
# print(tom.__dict__)   # tom.__dict__ 对象的命名空间中的所有名字
# tom.eat()   # 先找自己对象的内存空间 再找类的空间 再找父类的空间
# tom.climb()   # 先找自己的内存空间 再找类的空间
View Code

4,self.名字的时候,不要看self当前在哪个类里,要看这个self到底是谁的对象

class Parent:
    def func(self):
        print('in parent func')
    def __init__(self):
        self.func()

class Son(Parent):
    def func(self):
        print('in son func')

s = Son()
View Code

 5,object 类

class A:
#     '''
#     这是一个类
#     '''
    pass

a = A()
print(A.__dict__)  # 双下方法 魔术方法
# 创建一个空对象
# 调用init方法    —— 调用了么? 调用了
# 将初始化之后的对象返回调用处
View Code

6,在单继承中,如何给一个类的对象增加派生属性呢?

详见如下代码:

class Animal:
    def __init__(self,name,hp,ad):
        self.name = name     # 对象属性 属性
        self.hp = hp         #血量
        self.ad = ad         #攻击力
    def eat(self):
        print('eating in Animal')
        self.hp += 20

class Person(Animal):
    def __init__(self,name,hp,ad,sex):
        # Animal.__init__(self,name,hp,ad)
        #次为方法一
        # super(Person,self).__init__(name,hp,ad)
        #此为方法二
        super().__init__(name, hp, ad)
        #此为方法二简写(常用)
        # 在单继承中,super负责找到当前类所在的父类,在这个时候不需要再手动传self
        self.sex = sex      # 这个就是增加的派生属性
        self.money = 100
    def attack(self,dog):   # 派生方法
        print("%s攻击了%s"%(self.name,dog.name))
    def eat(self):                         # 重写
        super().eat()  # 在类内 使用 super()方法找父类的方法
        print('eating in Person')
        self.money -= 50

class Dog(Animal):
    def __init__(self,name,hp,ad,kind):
        Animal.__init__(self,name,hp,ad)
        self.kind = kind    # 派生属性
    def bite(self,person):  # 派生方法
        print("%s咬了%s" % (self.name, person.name))


alex = Person("zhangsan",100,10,"male")   # 实例化
#print(alex.__dict__)
# tg = Dog('到哥',100,50,'藏獒')
# print(tg.__dict__)
# 父类有eat 子类没有
# alex.eat() # 找父类的
# 子类有eat 不管父类中有没有
# alex.eat() # 找子类的
# 当子类中有,但是我想要调父类的
# Animal.eat(alex)          #指名道姓
# super(Person,alex).eat()  # super(子类名,子类对象)方法   —— 一般不用
# 子类父类都有eat方法,我想执行父类的eat和子类的eat
#alex.eat()  # 执行子类的eat
View Code

7,多继承之钻石继承

对于多继承中的新式类,寻找名字的顺应该遵循广度优先。类名.mro() 可以查询广度优先的遍历顺序。

例如下面的程序:

class A:
    def func(self):
        print('A')
class B(A):
    pass
    # def func(self):
    #     print('B')
class C(A):
    pass
    # def func(self):
    #     print('C')
class D(B):
    pass
    # def func(self):
    #     print('D')
class E(B,C):
    pass
    # def func(self):
    #     print('E')
class F(D,E):
    pass
    # def func(self):
    #     print('F')
f = F()
f.func()
print(F.mro())  # 查看广度优先的遍历顺序,其遍历顺序为 F-D-E-B-C-A
View Code

【附】:pyrhon3 中的新式类遵循的广度优先算法图示

8,当遇到多继承和super时,应遵循以下的情况:

(1)找到这个对象对应的类。

(2)将这个类的所有父类都找到画成一个图

(3)根据图写出广度优先的顺序

(4)再看代码,看代码的时候要根据广度优先顺序图来找对应的super

例如以下程序:

class A:
    def func(self):
        print('A')
class B(A):
    def func(self):
        super().func()
        print('B')
class C(A):
    def func(self):
        super().func()
        print('C')
class D(B,C):
    def func(self):
        super().func()
        print('D')
d = D()
d.func()     #结果为 A-C-B-D
View Code

该程序分析过程如下:

【注】:supper(),在单继承中就是单纯的寻找父类,在多继承中就是根据子节点所在图的 mro 顺序找寻下一个类。

9,python 2中的经典类的多继承问题

【注】:经典类只在 python2 版本才存在,且必须不继承object。它在遍历的时候遵循深度优先算法,而且没有mro方法和super()方法。在python2 的版本中,必须手动继承object 类,才是新式类。它在遍历的时候遵循广度优先算法,在新式类中,有mro方法和super()方法,但是在2.X版本的解释器中,必须传参数(子类名,子类对象)。

【附】:python2 中的经典类中遵循的深度优先算法图示

二:多态

1,什么是多态?

在java中,多态就是在一个类之下发展出来的多个类的对象都可以作为参数传入这里。而在python中不需要刻意实现多态,因为python本身自带多态效果。

2,鸭子类型

不是通过具体的继承关系来约束某些类中必须有哪些方法名,而是通过一种约定俗成的概念来保证在多个类中相似的功能叫相同的名字。

class QQpay():
    def pay(self,money):
        print('使用qq支付了%s元'%money)

class Wechatpay():
    def pay(self,money):
        print('使用微信支付了%s元'%money)

def pay(pay_obj,money):
    pay_obj.pay(money)
a=QQpay()
pay(a,100)
View Code

三:封装

1,什么是封装?

封装就是隐藏对象的属性和实现细节,仅对外提供公共访问方式。

2,私有静态变量

首先定义一个私有的名字: 就是在私有的名字前面加两条下划线 ,如  __N = 'aaa'

class A:
    __N = 'aaa'  # 静态变量
    def func(self):
        print(A.__N)  # 在类的内部使用正常

a = A()
a.func()
print(A.__N)   # 在类的外部直接使用 报错
View Code

 

ps:一个私有的名字 在存储的过程中仍然会出现在  类名.__dict__中,所以我们仍然可以调用到。python对其的名字进行了修改: _类名__名字,只不过在类的外部调用 :需要“_类名__名字”去使用,在类的内部可以正常的使用名字,在类内 只要你的代码遇到__名字,就会被python解释器自动的转换成_类名__名字。

3,私有属性

class B:
    def __init__(self,name):
        self.__name = name
    def func(self):
        print('in func : %s'%self.__name)
b = B('alex')
print(b._B__name)
b.func()
View Code

4,私有方法

class C:
    def __wahaha(self):
        print('wahaha')
    def ADCa(self):
        self.__wahaha()
c = C()
c._C__wahaha()
c.ADCa()    #在内部调用私有方法
View Code

5,私有的名字不能被子类继承

class D:
    def __func(self):      # '_D__func'
        print('in func')

class E(D):
    def __init__(self):
        self.__func()      # '_E__func'
e = E()

#该程序运行后会报错,因为func方法被私有化后是不能被子类继承的
View Code
class D:
    def __init__(self):
        self.__func()
    def __func(self):
        print('in D')

class E(D):
    def __func(self):
        print('in E')
e = E()
# 私有的名字,在类内使用的时候,就是会变形成_该类名__方法名
# 以此为例 :没有双下换线会先找E中的func
# 但是有了双下划线,会在调用这个名字的类D中直接找_D__func
View Code

6,私有的用处

(1)当一个方法不想被子类继承的时候。

(2)有些属性或者方法不希望从外部被调用,只想提供给内部的方法使用

class Room:
    def __init__(self,name,price,length,width,hight):
        self.name = name
        self.price = price
        self.__length = length
        self.__width = width
        self.hight = hight
    def area(self):
        return self.__length*self.__width

r = Room('ads',22,10,10,10)
print(r.name)
print(r.price)
print(r.area())
View Code

 

posted @ 2018-04-13 20:56  扬帆起航111  阅读(1615)  评论(0编辑  收藏  举报