python面向对象编程——作用域,多继承【重点难点】

12.面向对象编程

面向对象技术简介

  • 类(Class): 用来描述具有相同的属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法。对象是类的实例。
  • **方法:**类中定义的函数。
  • **类变量:**类变量在整个实例化的对象中是公用的。类变量定义在类中且在函数体之外。类变量通常不作为实例变量使用。
  • **数据成员:**类变量或者实例变量用于处理类及其实例对象的相关的数据。
  • **方法重写:**如果从父类继承的方法不能满足子类的需求,可以对其进行改写,这个过程叫方法的覆盖(override),也称为方法的重写。
  • **局部变量:**定义在方法中的变量,只作用于当前实例的类。
  • **实例变量:**在类的声明中,属性是用变量来表示的,这种变量就称为实例变量,实例变量就是一个用 self 修饰的变量。
  • **继承:**即一个派生类(derived class)继承基类(base class)的字段和方法。继承也允许把一个派生类的对象作为一个基类对象对待。例如,有这样一个设计:一个Dog类型的对象派生自Animal类,这是模拟"是一个(is-a)"关系(例图,Dog是一个Animal)。
  • **实例化:**创建一个类的实例,类的具体对象。
  • **对象:**通过类定义的数据结构实例。对象包括两个数据成员(类变量和实例变量)和方法。

类的定义

class Person:
    name = "张三"
    age = 3  # 定义在这里的属性叫类属性,所有这个类的实例对象都会共有这个属性相当于java中的静态变量

    def sayHello(self):
        print("hello")


person = Person()


print(person.name)
print(person.age)
person.sayHello()

p2 = Person()
print(p2.name)
print(p2.age)

类属性的访问与修改

class Person:
    name = "张三"
    age = 3

    def sayHello(self):
        print("hello")


# 类属性可以通过类名.属性名的方式来访问和修改,实例化对象要想对类属性进行修改要通过
# 实例对象想要修改类属性必须通过 person.__class__.name  的方式进行访问
Person.name = "诸葛亮"
person = Person()
person.__class__.name = "张飞"
print(Person.name)

实例的属性

class Person:

    def __init__(self, name, age):  # __init__方法是一个魔术方法,当调用改造方法时会自动调用该方法
        self.name = name  # 通过self.定义的属性都是实例属性,self指实例对象本身相当于java中的this关键字
        self.age = age


person1 = Person("张三", 3)
person2 = Person("李四", 5)
person3 = Person("王五", 9)

print(person1.name, person1.age)
print(person2.name, person2.age)
print(person3.name, person3.age)

类方法

class Person:

    @classmethod  # 通过这个装饰器装饰的方法都是类方法,相当于Java中的静态方法,可以通过 类名.方法 的形式调用
    def sayHello(self):
        print("hello")


Person.sayHello()

虚假的私有属性与君子约束

class Person:

    def __init__(self, name, age):
        self.__name = name  # 通过 self.__变量名定义的属性是私有属性,无法直接通过.直接方法,需要定义set和get方法
        self.__age = age

    def getName(self):
        return self.__name

    def getAge(self):
        return self.__age

    def setName(self, name):
        self.__name = name

    def setAge(self, age):
        self.__age = age


person = Person("张三", 3)

print(person.getName())
print(person.getAge())

"""
其实在python中并没有真正的私有属性
虽然不能通过.属性名的方式访问
但是可以通过 obj._类名__属性名 的形式进行直接访问,所以python中的私有属性只是一种君子约束,一般我们只用一个下划线表示不希望被修改即可
"""
person._Person__name = "诸葛亮"
print(person.getName())

通过装饰器对get方法和Set方法优化

class Person:

    def __init__(self):
        pass

    @property  # 通过这个装饰器可以将方法属性化
    def name(self):
        return self.__name

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

    @name.setter  # 可以设置 setter  deleter  getter ...
    def name(self, name):
        print("setName方法被调用")
        self.__name = name

    @age.setter
    def age(self, age):
        print("setAge方法被调用")
        self.__age = age


person = Person()
person.name = "张三"  # 这样就可以通过像设置属性一样调用方法了
person.age = 3

print(person.name)
print(person.age)

继承(重点)

  • Python 同样支持类的继承,如果一种语言不支持继承,类就没有什么意义。
class Person:

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

    def func1(self):
        print("我是Person")


class Student(Person):

    def __init__(self, name, age, num):
        Person.__init__(self, name, age)  # 调用父类构造器
        self._num = num  # 子类自己的属性

    def study(self):  # 子类自己的方法
        print("好好学习天天向上")


student = Student("张三", 3, 123456)
print(student._name, student._age, student._num)
student.func1()
student.study()
  • 此外与java不同的是,python是一门多继承的语言
  • python的多继承,多继承的构造方法可以根据需要进行重写
class Person:

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

    def func1(self):
        print("我是Person")


class Son(Person):

    def __init__(self, name, age, gender):
        Person.__init__(self, name, age)
        self._gender = gender

    def son(self):
        print("在老子面前永远是儿子")


class Student(Person):

    def __init__(self, name, age, num):
        Person.__init__(self, name, age)  # 调用父类构造器
        self._num = num  # 子类自己的属性

    def study(self):  # 子类自己的方法
        print("好好学习天天向上")


class Me(Son, Student):

    # 如果不写__init__方法就会默认基础第一个继承类的构造
    def __init__(self, name, age, gender, num, gift):
        Son.__init__(self, name, age, gender)
        Student.__init__(self, name, age, num)
        self._gift = gift

    def me(self):
        print("我就是我颜色不一样的烟火")


me = Me("孙悟空", 756, "男", 123, "七十二变")

print(isinstance(me, Person))  # isinstance(a,b) 判断a是不是b的实例
print(isinstance(me, Student))
print(isinstance(me, Son))
print(me._name)
print(me._age)
print(me._gender)
print(me._gift)
me.func1()
me.study()
me.son()
me.me()

方法重写

  • 如果你的父类方法的功能不能满足你的需求,你可以在子类重写你父类的方法,实例如下:
\#!/usr/bin/python3  
class Parent:        # 定义父类    
    def myMethod(self):      
        print ('调用父类方法')   
class Child(Parent): # 定义子类    
    def myMethod(self):       
        print ('调用子类方法')   
        c = Child()          # 子类实例 
        c.myMethod()         # 子类调用重写方法 
        super(Child,c).myMethod() #用子类对象调用父类已被覆盖的方法

类的那些内置的魔术方法

类的专有方法:

  • _init_ : 构造函数,在生成对象时调用
  • _del_ : 析构函数,释放对象时使用
  • _repr_ : 打印,转换
  • _setitem_ : 按照索引赋值
  • _getitem_: 按照索引获取值
  • _len_: 获得长度
  • _cmp_: 比较运算
  • _call_: 函数调用
  • _add_: 加运算
  • _sub_: 减运算
  • _mul_: 乘运算
  • _truediv_: 除运算
  • _mod_: 求余运算
  • _pow_: 乘方

重写这些方法会在特定的条件下被调用,例如常用的_del_ 会在对生命周期结束时被回收时调用

_len_ 在调用len()函数时会被调用等

如果一个类同时继承了多个类有多个方法相同应该继承谁的呢?(难点)

在这里插入图片描述

如图所示是一个非常复杂的类继承关系图,代码展示如下

class A:
    def __init__(self,a):
        self.a = a
        
    def Hello(self):
        print("helloA")

class B:
    def __init__(self,a):
        self.a = a
        
    def Hello(self):
        print("helloB")
        
        
class C:
    def __init__(self,a):
        self.a = a
        
    def Hello(self):
        print("helloC")
        
class D(A):
    def __init__(self,a,b):
        A.__init__(self,a)
        self.b = b  
        
    def Hai(self):
        print("HiD")
        
class E(B,C):
    def __init__(self,a,b):
        C.__init__(self,a)
        self.b = b
        
    def Hello(self):
        print("HelloC")
        
    def Hai(self):
        print("HiE")

class F(D,E,C):
    def __init__(self,a,b,c):
        D.__init__(self,a,b)
        self.c = c
        
  • 此时F的调用构造方法,首先它会在自己的类中找有没有重写 __init__ 方法,如果有则调用该方法
  • 然后实例分别调用Hello()和Hai() 方法该调用谁的方法呢?
    1. 首先这个实例会在F类里找,看看有没有重写这两个方法,发现没有重写这两个方法,这时候会到D类里找
    2. 如果D类里恰巧有这两个方法,则会直接使用D类的这两个方法,如果D类不存在该重写方法,则会去D类的父类也就是A类中寻找,如果在A类中找到了对应的方法就会使用A类的方法,如果没找到就会去object类里找。
    3. 如果在Object类中没有找到才会去E类中找,如果E类中没找到就会去E类的父类中找,E类的第一个父类中没找到就会去找第二个父类,然后一直找到Object
    4. 全部都没找到才会去C类中找,知道找到为止,如果找完了所有的类都没有找到该方法,则会报错
    5. F类找方法的路线解析:(在寻找路线的任任何一个类中找到了匹配的方法将不会继续往后边寻找。)
      • F => D => A => Object => E => B => C => Object => C => Object
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Pointer-faker

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值