【Python基础入门系列】12、Python中的面向对象(三)

一、继承的简介

1.引入

class Doctor():
    name = ''
    age = ''
    def treat(self):
        print('treat a patient')

class soldier():
    name = ''
    age = ''

    def protect(self):
        print('Protect the people')

以上定义了两个类,但是又重复的属性name、age,如果每定义一个类都要分别定义这两个属性,很重复冗余,所以可以抽象出一个类来封装它们的公共属性和方法,即它们的父类这些类都继承自它们的父类。

class Person():
    name = ''
    age = ''

2.继承的特点:

1.提高了代码的复用性;
2.让类与类之间产生联系,有了这个联系,才有了多态。
继承是面向对象的三大特性之一,其他两个特性是封装、多态。

定义一个动物类

class Animal:
    def run(self):
        print('Running...')

    def sleep(self):
        print('Sleeping...')

a = Animal()
a.run()

再定义一个狗类,

class Dog:
    def run(self):
        print('Running...')

    def sleep(self):
        print('Sleeping...')

这样定义狗类,有大量重复性代码,表现较差,我们应该使狗类继承自动物类,继承动物类的属性和方法。

class Dog(Animal):

    pass

d = Dog()
d.run()
d.sleep()

打印

Running...
Sleeping...

在定义类时可以在类名+括号,括号内的指定的是当前类的父类(超类、基类,super)。

class Dog(Animal):
    def Housekeep(self):
        print('Housekeeping...')

d = Dog()
d.run()
d.sleep()
d.Housekeep()
r1 = isinstance(d,Dog)
r2 = isinstance(d,Animal)
print(r1,r2)

打印

Running...
Sleeping...
Housekeeping...
True True

显然,d既是Dog的实例,也是Animal的实例,所以Dog也继承自了Animal。
如果在创建类的时候省略了父类,则默认父类是object;
object是所有类的父类,所有类都继承自object。
issubclass()检查一个类是否是另一个类的子类。

class Animal:
    def run(self):
        print('Running...')

    def sleep(self):
        print('Sleeping...')

class Dog(Animal):
    def Housekeep(self):
        print('Housekeeping')

r1 = issubclass(Dog,Animal)
r2 = issubclass(Animal,object)
print(r1,r2)

证明了所有类都是object的子类,所有对象都是object的实例。

二、方法的重写

如果在子类中有和父类重名的方法,通过子类的实例去调用方法时,会调用子类的方法而不是父类的方法,这个特点称为方法的重写(覆盖,override)。
如,

class Animal:
    def run(self):
        print('Running...')

    def sleep(self):
        print('Sleeping...')

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

    def sleep(self):
        print('Dog is sleeping...')

    def Housekeep(self):
        print('Dog is housekeeping')

d = Dog()
d.run()

打印Dog is running...
下边再详细说明,

class A(object):
    def test(self):
        print('A...')

class B(A):
    pass

class C(B):
    pass

c = C()
c.test()

打印A...
类B中加入方法test()后,

class A(object):
    def test(self):
        print('A...')

class B(A):
    def test(self):
        print('B...')

class C(B):
    pass

c = C()
c.test()

打印B...
类C中加入方法test()后,

class A(object):
    def test(self):
        print('A...')

class B(A):
    def test(self):
        print('B...')

class C(B):
    def test(self):
        print('C...')

c = C()
c.test()

打印C...
当我们调用一个对象时,会有先去当前对象寻找是否有该方法,如果有直接调用;如果没有,则取当前对象的父类中寻找,如果有,直接调用父类中的方法,如果没有,则去父类的父类寻找,如果有直接调用,以此类推,直到找到object,如果依然没有则报错。

三、super()

父类中所有的方法都会被子类继承,包括特殊方法,如__init__()等,也可以重写特殊方法。

class Animal:
    def __init__(self,name):
        self._name = name

    def run(self):
        print('Running...')

    def sleep(self):
        print('Sleeping...')

    @property
    def name(self):
        return self._name

    @name.setter
    def name(self,name):
        self._name = name

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

    def sleep(self):
        print('Dog is sleeping...')

    def Housekeep(self):
        print('Dog is housekeeping')

d = Dog()
d.run()

会抛出异常,TypeError: __init__() missing 1 required positional argument: 'name'
因为初始化没有传入参数,因为父类初始化要传入name这个参数,子类也必须传入。
传入参数后,

class Animal:
    def __init__(self,name):
        self._name = name

    def run(self):
        print('Running...')

    def sleep(self):
        print('Sleeping...')

    @property
    def name(self):
        return self._name

    @name.setter
    def name(self,name):
        self._name = name

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

    def sleep(self):
        print('Dog is sleeping...')

    def Housekeep(self):
        print('Dog is housekeeping')

d = Dog('erha')
print(d.name)

打印erha
调用setter方法修改属性,

class Animal:
    def __init__(self,name):
        self._name = name

    def run(self):
        print('Running...')

    def sleep(self):
        print('Sleeping...')

    @property
    def name(self):
        return self._name

    @name.setter
    def name(self,name):
        self._name = name

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

    def sleep(self):
        print('Dog is sleeping...')

    def Housekeep(self):
        print('Dog is housekeeping')

d = Dog('erha')
d.name = 'demu'
print(d.name)

打印demu
如果需要在子类中定义新的属性时,即要扩展属性时,

class Animal:
    def __init__(self,name):
        self._name = name

    def run(self):
        print('Running...')

    def sleep(self):
        print('Sleeping...')

    @property
    def name(self):
        return self._name

    @name.setter
    def name(self,name):
        self._name = name

class Dog(Animal):
    def __init__(self,name,age):
        self._name = name
        self._age = age

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

    def sleep(self):
        print('Dog is sleeping...')

    def Housekeep(self):
        print('Dog is housekeeping')

    @property
    def age(self):
        return self._age

    @age.setter
    def age(self, age):
        self._age = age

d = Dog('erha',10)
print(d.name)
print(d.age)

打印

erha
10

可以直接调用父类中的__init__()来初始化父类中的属性,

class Animal:
    def __init__(self,name):
        self._name = name

    def run(self):
        print('Running...')

    def sleep(self):
        print('Sleeping...')

    @property
    def name(self):
        return self._name

    @name.setter
    def name(self,name):
        self._name = name

class Dog(Animal):
    def __init__(self,name,age):
        Animal.__init__(self,name)
        self._age = age

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

    def sleep(self):
        print('Dog is sleeping...')

    def Housekeep(self):
        print('Dog is housekeeping')

    @property
    def age(self):
        return self._age

    @age.setter
    def age(self, age):
        self._age = age

d = Dog('erha',10)
print(d.name)
print(d.age)

即Dog类中的初始化方法中,name属性通过Animal的初始化调用获取,可以将父类的多个属性传给子类,但是继承的父类是固定的,这是需要用suoer()动态地继承父类属性。
super()可以用来获取当前类的父类,并且通过suoer()返回的对象,调用父类方法时不需要传入self。

class Animal:
    def __init__(self,name):
        self._name = name

    def run(self):
        print('Running...')

    def sleep(self):
        print('Sleeping...')

    @property
    def name(self):
        return self._name

    @name.setter
    def name(self,name):
        self._name = name

class Dog(Animal):
    def __init__(self,name,age):
        super().__init__(name)
        self._age = age

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

    def sleep(self):
        print('Dog is sleeping...')

    def Housekeep(self):
        print('Dog is housekeeping')

    @property
    def age(self):
        return self._age

    @age.setter
    def age(self, age):
        self._age = age

d = Dog('erha',10)
print(d.name)
print(d.age)

执行结果与之前相同。

四、多重继承

语法:类名.__bases__
可以用来获取当前类的所有直接父类。

class A(object):
    def test(self):
        print('A...')

class B(A):
    def test2(self):
        print('B...')

class C(B):
    pass

print(C.__bases__)
print(B.__bases__)
print(A.__bases__)

打印

(<class '__main__.B'>,)
(<class '__main__.A'>,)
(<class 'object'>,)

在Python中支持多重继承,即可以为一个类同时指定多个父类。

class A(object):
    def test(self):
        print('A...')

class B(object):
    def test2(self):
        print('B...')

class C(A,B):
    pass

print(C.__bases__)

打印(<class '__main__.A'>, <class '__main__.B'>)

class A(object):
    def test(self):
        print('A...')

class B(object):
    def test2(self):
        print('B...')

class C(A,B):
    pass

c = C()
c.test()
c.test2()

打印

A...
B...

显然,子类可以同时继承多个父类的方法。
但是一般在实际中没有特殊情况,尽量避免使用多重继承,因为多重继承会让代码过于复杂;
如果多个父类中有同名的方法,则会在第一个父类中寻找,找不到再继续往下寻找…
如,

class A(object):
    def test(self):
        print('A...test')

class B(object):
    def test(self):
        print('B...test')

    def test2(self):
        print('B...')

class C(A,B):
    pass

c = C()
c.test()

会打印A...test
如果A中没有test()方法,

class A(object):
    pass

class B(object):
    def test(self):
        print('B...test')

    def test2(self):
        print('B...')

class C(A,B):
    pass

c = C()
c.test()

会打印B...test

五、多态

多态是面向对象的三大特征之一。
多态字面理解即多种形态,在面向对象中指一个对象可以以不同的形态呈现。

class A(object):
    def __init__(self,name):
        self._name = name

    @property
    def name(self):
        return self._name

    @name.setter
    def name(self,name):
        self._name = name


class B(object):
    def __init__(self, name):
        self._name = name

    @property
    def name(self):
        return self._name

    @name.setter
    def name(self, name):
        self._name = name

def speak(obj):
    print('Hello,%s' % obj.name)

a = A('Tom')
b = B('Jerry')
speak(a)
speak(b)

打印

Hello,Tom
Hello,Jerry

定义一个新类C,但是没有name属性时,

class A(object):
    def __init__(self,name):
        self._name = name

    @property
    def name(self):
        return self._name

    @name.setter
    def name(self,name):
        self._name = name


class B(object):
    def __init__(self, name):
        self._name = name

    @property
    def name(self):
        return self._name

    @name.setter
    def name(self, name):
        self._name = name

class C(object):
    pass
def speak(obj):
    print('Hello,%s' % obj.name)

a = A('Tom')
b = B('Jerry')
c= C()
speak(a)
speak(b)
speak(c)

会抛出异常,AttributeError: 'C' object has no attribute 'name'
增加类型检查,

class A(object):
    def __init__(self,name):
        self._name = name

    @property
    def name(self):
        return self._name

    @name.setter
    def name(self,name):
        self._name = name


class B(object):
    def __init__(self, name):
        self._name = name

    @property
    def name(self):
        return self._name

    @name.setter
    def name(self, name):
        self._name = name

class C(object):
    pass

def speak(obj):
    print('Hello,%s' % obj.name)

def test_speak(obj):
    '''类型检查'''
    if isinstance(obj,A):
        print('Hello,%s' % obj.name)

a = A('Tom')
b = B('Jerry')
c= C()
test_speak(a)
test_speak(b)
test_speak(c)

打印Hello,Tom
test_speak()这个函数中做了类型检查,也就是obj是A类型的对象的时候,才可以正常执行,其他类型的对象无法使用该函数。
这个函数其实就是违反了多态。
违反了多态的函数,只适合于一种类型的对象,无法适用于其他类型的对象,导致函数的适用性非常差。
多态使面向对象的编程更加具有灵活性

l = [1,2,3]
s = 'python'
print(len(l),len(s))

如上,len()函数既可以得到list的长度,又可以获取str的长度,
len()就可以检查不同对象类型的长度就是面向对象的特征之一——多态。
len()的底层实现:
之所以len()函数能获取长度,是因为这些对象中有一个特殊方法len__()方法;换句话说,只要对象中有__len()方法,就可以通过len()方法来获取对象的长度。

class B(object):
    def __init__(self, name):
        self._name = name

    @property
    def name(self):
        return self._name

    @name.setter
    def name(self, name):
        self._name = name

b = B('Jerry')
print(len(b))

会报错TypeError: object of type 'B' has no len()
在B中加入__len__()方法后,

class B(object):
    def __init__(self, name):
        self._name = name

    @property
    def name(self):
        return self._name

    @name.setter
    def name(self, name):
        self._name = name

    def __len__(self):
        return 1

b = B('Jerry')
print(len(b))

会打印1

面向对象的三大特征
   1.封装:确保对象中的数据更安全;
   2.继承:保证对象的可扩展性;
   3.多态:保证了程序的灵活性。

六、类中的属性和方法

类属性是直接在类中定义的属性,类属性可以通过类或类的实例访问;
通过实例对象添加的属性属于实例属性。

class A(object):
    #类属性
    count = 0

a = A()
print(A.count)
print(a.count)

打印

0
0

修改实例的属性,

class A(object):
    #类属性
    count = 0

a = A()
a.count = 5
print('In class,',A.count)
print('In instance,',a.count)

打印

In class, 0
In instance, 5

修改类中的属性,

class A(object):
    #类属性
    count = 0

a = A()
A.count = 5
print('In class,',A.count)
print('In instance,',a.count)

打印

In class, 5
In instance, 5

可总结出:类属性只能通过类对象来修改,无法通过实例对象来修改;

class A(object):
    def __init__(self):
        #实例属性
        self.name = 'Tom'

a = A()
print(a.name)

打印Tom
在初始化中定义的属性为实例属性,只能通过实例属性来访问和修改,类对象无法访问和修改。
如,

class A(object):
    def __init__(self):
        #实例属性
        self.name = 'Tom'

a = A()
print(A.name)

报错AttributeError: type object 'A' has no attribute 'name'
在类中定义的以self为第一个参数的方法都是实例方法;
实力方法在调用时,Python会将调用对象作为self传入;

class A(object):
    def __init__(self):
        #实例属性
        self.name = 'Tom'
    def test(self):
        #实例方法
        print('in test')
a = A()
a.test()

打印in test
类对象调用时,

class A(object):
    def __init__(self):
        #实例属性
        self.name = 'Tom'
    def test(self):
        #实例方法
        print('in test')
a = A()
A.test()

报错TypeError: test() missing 1 required positional argument: 'self'
但是通过类对象调用时,传入实例对象多为方法的参数不会报错。
即通过类对象调用方法时,不会自动穿self,必须手动传self。

class A(object):
    def __init__(self):
        #实例属性
        self.name = 'Tom'
    def test(self):
        #实例方法
        print('in test')
a = A()
A.test(a)

输出结果相同。
a.test()等价于A.test(a)
可以通过classmethod装饰器来调用类方法;
类方法的第一个参数是cls,也会被自动传递,cls就是当前的类对象。

class A(object):
    count = 0
    def __init__(self):
        #实例属性
        self.name = 'Tom'
    def test(self):
        #实例方法
        print('in test')
    @classmethod
    def test2(cls):
        #实例方法
        print('in test2',cls)

a = A()
A.test2()

打印in test2 <class '__main__.A'>
调用类属性,

class A(object):
    count = 0
    def __init__(self):
        #实例属性
        self.name = 'Tom'
    def test(self):
        #实例方法
        print('in test')
    @classmethod
    def test2(cls):
        #实例方法
        print('in test2',cls)
        print(cls.count)

a = A()
A.test2()

打印

in test2 <class '__main__.A'>
0

通过实例调用类方法,

class A(object):
    count = 0
    def __init__(self):
        #实例属性
        self.name = 'Tom'
    def test(self):
        #实例方法
        print('in test')
    @classmethod
    def test2(cls):
        #实例方法
        print('in test2',cls)
        print(cls.count)

a = A()
a.test2()

结果相同。
可总结:类方法可以通过类对象调用,也可通过实例方法调用。
A.test2()等价于a.test2()
静态方法:
staticmethod装饰器来调用类方法

class A(object):
    count = 0
    def __init__(self):
        #实例属性
        self.name = 'Tom'
    def test(self):
        #实例方法
        print('in test')
    @classmethod
    def test2(cls):
        #实例方法
        print('in test2',cls)
        print(cls.count)
    @staticmethod
    def test3():
        print('in test3')

a = A()
A.test3()
a.test3()

打印

in test3
in test3
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值