day20-面向对象编程、继承


一、面向对象编程

1、简介
  面向对象编程——Object Oriented Programming,简称OOP,是一种程序设计思想。OOP把对象作为程序的基本单元,一个对象包含了数据和操作数据的函数。
面向过程的程序设计把计算机程序视为一系列的命令集合,即一组函数的顺序执行。为了简化程序设计,面向过程把函数继续切分为子函数,即把大块函数通过切割成小块函数来降低系统的复杂度。
  而面向对象的程序设计把计算机程序视为一组对象的集合,而每个对象都可以接收其他对象发过来的消息,并处理这些消息,计算机程序的执行就是一系列消息在各个对象之间传递。
  在Python中,所有数据类型都可以视为对象,当然也可以自定义对象。自定义的对象数据类型就是面向对象中的类(Class)的概念。

2、类的构成
类Class由3部分构成
类的名称:类名
类的属性:一组数据
类的方法:允许对类进行操作的方法(行为)

类里面的变量可以叫做静态属性、静态变量、静态字段
类里面的函数一般叫做方法

3、定义类
定义一个类,格式如下:
class 类名:
  静态属性
  动态方法


4、举例:

class Person:   #定义一个人类
    role = 'person'  #人的角色属性都是人
    def walk(self):  #人都可以走路,也就是有一个走路方法
        print("person is walking...")

print(Person.role)  #查看人的role属性
print(Person.walk)  #引用人的走路方法,注意,这里不是在调用

5、__init__方法
  由于类可以起到模板的作用,因此,可以在创建实例的时候,把一些我们认为必须绑定的属性强制填写进去。通过定义一个特殊的__init__方法,在创建实例的时候,就把name,score等属性绑上去:

class Student:
    def __init__(self, name, score):
        self.name = name
        self.score = score

注意:特殊方法“init”前后有两个下划线!!!
注意到__init__方法的第一个参数永远是self,表示创建的实例本身,因此,在__init__方法内部,就可以把各种属性绑定到self,因为self就指向创建的实例本身。
有了__init__方法,在创建实例的时候,就不能传入空的参数了,必须传入与__init__方法匹配的参数,但self不需要传,Python解释器自己会把实例变量传进去。

6、实例化对象
类名加括号就是实例化,会自动触发__init__函数的运行,可以用它来为每个实例定制自己的特征

实例化对象的过程:
1)在内存中创建了一个内存空间,存储类
2)在这个类内存空间中创建静态变量和特殊方法__init__的内存地址,和动态方法的内存地址
3)在内存中创建了一个内存空间,存储这个变量

一般情况下:
类中的静态属性通过类名去调用或修改
类中的动态方法通过对象去调用执行

class 类名:
    def __init__(self,参数1,参数2):
        self.对象的属性1 = 参数1
        self.对象的属性2 = 参数2

    def 方法名(self):pass
    def 方法名2(self):pass

对象名 = 类名(1,2) #对象就是实例,代表一个具体的东西
         #类名() : 类名+括号就是实例化一个类,相当于调用了__init__方法
        #括号里传参数,参数不需要传self,其他与init中的形参一一对应
        #结果返回一个对象
对象名.对象的属性1#查看对象的属性,直接用 对象名.属性名 即可

对象名.方法名() #调用类中的方法,直接用 对象名.方法名() 即可

例子:

class Person:   #定义一个人类
    role = 'person'  #人的角色属性都是人
    def __init__(self,name):
        self.name = name  # 每一个角色都有自己的昵称;
        
    def walk(self):  #人都可以走路,也就是有一个走路方法
        print("person is walking...")

p1 = Person('Mike')  #实例化人p1
print(p1.role)  #查看人的role属性
print(p1.name)  #查看人的name属性
p1.walk()  #引用人的走路方法

实例化的过程就是类——>对象的过程

7、self
self:在实例化时自动将对象/实例本身传给__init__的第一个参数,你也可以给他起个别的名字,但是约定俗成都这么写。

8、类属性的补充

8.1、我们定义的类的属性到底存到哪里了?有两种方式查看
dir(类名):查出的是一个名字列表
类名.__dict__:查出的是一个字典,key为属性名,value为属性值

print(dir(Person))
#['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__',
# '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', 
# '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'role', 'walk']

print(Person.__dict__)
#{'__module__': '__main__', 'role': 'person', '__init__': <function Person.__init__ at 0x0054FA98>,
# 'walk': <function Person.walk at 0x0054FA50>, '__dict__': <attribute '__dict__' of 'Person' objects>,
#  '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__doc__': None}

8.2、特殊的类属性
类名.__name__# 类的名字(字符串)
类名.__doc__# 类的文档字符串
类名.__base__# 类的第一个父类(在讲继承时会讲)
类名.__bases__# 类所有父类构成的元组(在讲继承时会讲)
类名.__dict__# 类的字典属性
类名.__module__# 类定义所在的模块
类名.__class__# 实例对应的类(仅新式类中)


9、类名称空间与对象的名称空间
创建一个类就会创建一个类的名称空间,用来存储类中定义的所有名字,这些名字称为类的属性

而类有两种属性:静态属性和动态属性

静态属性就是直接在类中定义的变量
动态属性就是定义在类中的方法

其中类的数据属性也叫静态属性是共享给所有对象的,通过id可以看出在内存中的地址是一样的

print(id(Person.role))
#31924864
print(id(p1.role))
#31924864

而类的动态属性是绑定到所有对象的,通过引用类的动态方法可以看出方法在内存中的地址是不一样的

print(Person('Tom').walk)
#<bound method Person.walk of <__main__.Person object at 0x01E83050>>
print(p1.walk)
#<bound method Person.walk of <__main__.Person object at 0x01E72F70>>

创建一个对象/实例就会创建一个对象/实例的名称空间,存放对象/实例的名字,称为对象/实例的属性
在obj.name会先从obj自己的名称空间里找name,找不到则去类中找,类也找不到就找父类...最后都找不到就抛出异常

二、面向对象的三大特性:继承,多态,封装

2.1继承

继承是一种创建新类的方式,在python中,新建的类可以继承一个或多个父类,父类又可称为基类或超类,新建的类称为派生类或子类
python中类的继承分为:单继承和多继承

2.2、单继承
比如,我们已经编写了一个名为Animal的class,有一个run()方法可以直接打印:

class Animal:
    def run(self):
        print('Animal is running...')

当我们需要编写Dog和Cat类时,就可以直接从Animal类继承:

class Dog(Animal):
    pass
class Cat(Animal):
    pass

对于Dog来说,Animal就是它的父类,对于Animal来说,Dog就是它的子类。Cat和Dog类似。

继承有什么好处?最大的好处是子类获得了父类的全部功能。
由于Animial实现了run()方法,因此,Dog和Cat作为它的子类,什么事也没干,就自动拥有了run()方法:

dog = Dog()
dog.run()

cat = Cat()
cat.run()

运行结果如下:
Animal is running...
Animal is running...

继承的第二个好处需要我们对代码做一点改进。你看到了,无论是Dog还是Cat,它们run()的时候,显示的都是Animal is running...,符合逻辑的做法是分别显示Dog is running...和Cat is running...,因此,对Dog和Cat类改进如下:

class Dog(Animal):
    def run(self):
        print('Dog is running...')

class Cat(Animal):
    def run(self):
        print('Cat is running...')

再次运行,结果如下:
Dog is running...
Cat is running...

在单继承中,如果只想执行父类的属性或方法,那么子类的属性名或方法不能与父类中的属性名或方法名重复,否则只会执行子类中的属性或方法,相当于重写
如果既想执行子类的方法,又想执行父类中的方法,有2种方法:
方法1、使用super

class A:
    def func(self):
        print('in A')

class B(A):
    def func(self):
        super().func()
        print('in B')

b1 = B()
b1.func()
结果:
in A
in B

方法2、方法内调用

class A:
    def func(self):
        print('in A')

class B(A):
    def func(self):
        A.func(self)
        print('in B')

b1 = B()
b1.func()    
结果:
in A
in B

2.3、多继承
继承还可以一级一级地继承下来,就好比从爷爷到爸爸、再到儿子这样的关系。而任何类,最终都可以追溯到根类object,这些继承关系看上去就像一颗倒着的树。比如如下的继承树:

class Base:
    def test(self):
        print('---Base---')

class A(Base):
    def test(self):
        print('---A---')
    def testA(self):
        print('---A---')

class B(Base):
    def test(self):
        print('---A---')
    def testB(self):
        print('---B---')

class C(A, B):
    pass

c = C()
c.test()

类A和类B都继承类Base,类C继承类A和B
调用类C的test方法时,依次查看的顺序是类A,类B,类Base
如果定义类C时写成class C(B, A):,则先查看B,再查看A

print(C.__mro__)  #在Python3中可以查看C类的对象搜索方法时的先后顺序。
(<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class '__main__.Base'>, <class 'object'>)

2.3、查看继承

>>> SubClass1.__bases__ #__base__只查看从左到右继承的第一个子类,__bases__则是查看所有继承的父类
(<class '__main__.ParentClass1'>,)
>>> SubClass2.__bases__
(<class '__main__.ParentClass1'>, <class '__main__.ParentClass2'>)

提示:如果没有指定基类,python的类会默认继承object类,object是所有python类的基类,它提供了一些常见方法(如__str__)的实现。

>>> ParentClass1.__bases__
(<class 'object'>,)
>>> ParentClass2.__bases__
(<class 'object'>,)

当子类和父类都存在相同的run()方法时,我们说,子类的run()覆盖了父类的run(),在代码运行的时候,总是会调用子类的run()。这样,我们就获得了继承的另一个好处:多态。

继承可以把父类的所有功能都直接拿过来,这样就不必重零做起,子类只需要新增自己特有的方法,也可以把父类不适合的方法覆盖重写。

2.4、重写
就是子类中,有一个和父类相同名字的方法,在子类中的方法会覆盖掉父类中同名的方法

class Cat:
        def sayHello(self):
                print("halou...")
class Bosi(Cat):
        def sayhello(self):
                #调用父类的方法sayHello,在python2和3中都可以用,在参数中需要加self
                 Cat.sayHello(self)
                 #调用父类的方法sayHello,在python3中可以用,参数不需要加self,2种方法都可以
                 #super().sayHello()
                 print("hello...")
bosi = Bosi()
bosi.sayhello()

halou...
hello...

2.5、私有属性不会被继承,公有属性会被继承

私有属性和私有方法只有在同一个类内部才能够继承,在外部不能继承

class 类1:
    def 方法1(self):
        self.属性1
        self.__属性2
    def __方法2(self):
        self.方法1() #在相同类中的不同方法中,属性1可以继承,属性2也可以继承
    def 方法3(self):
        self.方法1() #因为是在同一个类中,所以方法1可以继承,包括公有方法1中的公有属性1和私有属性2
        self.__方法2() #因为是在同一个类中,方法2可以继承

class 类2(类1):  #继承类1
    def 方法1(self):
        self.属性1  #在不同类中公有属性1可以继承
        self.__属性2 #在不同类中私有属性2不能继承
    def 方法3(self):
        self.方法1() #在不同类中公有方法1可以继承,包括公有方法1中的公有属性1和私有属性2
        self.__方法2() #在不同类中私有方法2不能继承

aa = 类1()
bb = 类2()

类1中
  方法1是公有方法
    属性1是公有属性,在不同的方法和不同的类中都可以继承
    属性2是私有属性,在不同的类中不可以继承,在同一个类中不同方法中可以继承
  __方法2是私有方法,在同一个类中可以继承,在不同的类中不可以继承

优质内容筛选与推荐>>
1、查找整数
2、tsung日志文件说明
3、JavaWeb总结(九)
4、61条面向对象设计的经验原则
5、领域模型管理与AOP


长按二维码向我转账

受苹果公司新规定影响,微信 iOS 版的赞赏功能被关闭,可通过二维码转账支持公众号。

    阅读
    好看
    已推荐到看一看
    你的朋友可以在“发现”-“看一看”看到你认为好看的文章。
    已取消,“好看”想法已同步删除
    已推荐到看一看 和朋友分享想法
    最多200字,当前共 发送

    已发送

    朋友将在看一看看到

    确定
    分享你的想法...
    取消

    分享想法到看一看

    确定
    最多200字,当前共

    发送中

    网络异常,请稍后重试

    微信扫一扫
    关注该公众号





    联系我们

    欢迎来到TinyMind。

    关于TinyMind的内容或商务合作、网站建议,举报不良信息等均可联系我们。

    TinyMind客服邮箱:support@tinymind.net.cn