Python面向对象编程深度解析

文章目录

  • 一、面向对象基本语法
  • 二、成员与类变量
  • 三、方法
  • 四、私有
  • 五、特殊成员
  • 六、继承
  • 七、反射
  • 八、约束
  • 九、三元表达式
  • 一、面向对象基本语法

    1、面向对象的核心思想

    2、面向对象基本语法
    创建类和对象:
    创建类:class 类名, 类名要用驼峰式(单词首字母大写)
    创建对象:类名()

    # 创建类
    class Car:
        print('我是造车图纸')
    
    # 创建对象
    target = Car()     # 创建对象的时候会自动执行类里面的内容
    -------------------------------------
    我是造车图纸
    

    类中有两样内容
    属性:用来初始化一个对象
    动作:用来创建一个对象所拥有的功能,常被写成函数(类中的函数被称作方法)

    # 创建类,类名:Car
    class Car:
        # 创建属性__init__(self),用来初始化这个对象:通过self来初始化对象中的变量,self相当于js/php中的this
        def __init__(self, color, name):   # color,type就是对象中的变量
            print('我是属性')
            # self给车添加属性
            self.color = color             # 被初始化的变量就叫做实例变量,比如:self.color
            self.type = name
            slef.age  = 23                 # 可以自定义一个初始化变量
    
        # 创建动作(方法),用来描述对象所拥有的功能
        def run(self):
            print('我能跑')
    
    # 创建对象,把参数传给属性        
    target = Car('红色','奔驰')
    

    访问类中的方法
    self.方法(self就是target)

    # 创建类,类名:Car
    class Car:
        # 创建属性__init__(self),用来初始化这个对象:通过self来初始化对象中的变量
        def __init__(self, color, name):   # color,type就是对象中的变量
            print('我是属性')
            print('self对象的ID:',self)
            # self给车添加属性
            self.color = color             # 被初始化的变量就叫做实例变量,比如:self.color
            self.type = name
    
        # 创建动作(方法),用来描述对象所拥有的功能
        def run(self):
            print('我能跑')
    
    # 创建对象target,对象负责把值传给属性__init__并进行初始化,属性中的self就是target对象,它们的id一样
    target = Car('红色','奔驰')
    print('terget对象的ID:',target)
    
    # 使用对象访问类中的方法
    target.run()
    -------------------------------
    我是属性                       # 默认会执行属性__init__(self)里面的内容
    self对象的ID:   <__main__.Car object at 0x0000012DF92C4828>  # 说明self就是target
    terget对象的ID: <__main__.Car object at 0x0000012DF92C4828>
    我能跑                         # 调用类中的方法
    
    

    3、造对象的过程(原理)
    我们创建类的时候,默认生成一个__new__,但是不显示
    当我们使用Foo() 去创建一个对象的时候,会优先执行__new__,并在__new__里面开辟一块内存(相当于开了一块地),然后__init__在这个内存地址里面进行初始化对象的操作。

    二、成员与类变量

    1、成员与类变量
    实例变量:被对象初始化过的变量,通过对象调用:【对象.变量】
    类变量:在类中定义的变量,作用于整个类,通过类名调用:【类名.变量】

    class Car:
        age = 23                          # 类变量,作用于整个类中
        def __init__(self, color, name):
            self.color = color            # 实例变量,被初始化的变量,比如:self.color
            self.type = name
    
        def run(self):
            print('我能跑')
    
    # 创建对象
    target = Car('红色','奔驰')
    print(target.color)  # 通过对象去访问实例变量,就相当于self.color
    print(Car.age)       # 通过类名访问类变量
    --------------------------------------------
    红色
    23
    

    三、方法

    一、方法,其实就是类中的函数。
    1、实例方法
    通过对象调用,【对象.方法】

    哪个对象调用这个方法,self就是那个对象

    class Car:
        age = 23
        def __init__(self, color, name):
            self.color = color
            self.type = name
    
        # 创建实例方法
        def run(self):
            # 通过对象调用实例变量,通过类名调用类变量
            print(f'我叫{self.type},我今年{Car.age}岁了,我是{self.color}的')
    
    # 创建对象
    target = Car('红色','奔驰')
    # 调用实例方法 [对象.方法]
    target.run() 
    -------------------------------------
    我叫奔驰,我今年23岁了,我是红色的
    

    1.1 验证这句话【哪个对象正在调用这个方法,self就是那个对象】

    class Func:
    
        def fangfa(self):
            print(self)    # 打印self
    
    F = Func()             # 造对象
    F.fangfa()             # 用对象访问方法
    print(F)               # 打印对象 print(Func())
    -------------------------------------------------
    <__main__.Func object at 0x000001CE899452B0>        # 打印的self
    <__main__.Func object at 0x000001CE899452B0>        # 打印的对象F
    

    2、类方法
    通过类名调用, 类名.方法
    必须在上面加上一个@classmethod装饰器,第一个参数是cls,cls接收一个类名(cls==类名)
    一般用来创建对象: cls() == 类名()
    哪个类名正在调用这个类方法,cls就是那个类名

    class Car:
        age = 23
        def __init__(self, color, name):
            self.color = color
            self.type = name
    
        # 创建实例方法
        @classmethod
        def run(cls,var1,var2):   # 里面可以带参数
            print(f'我是类方法{var1}{var2}')
            print('打印cls:',cls)
    
    # 调用类方法 [类名.方法]
    Car.run('哈哈','呵呵')
    print('打印类名:',Car)       # 类名ID与clsID一样,所以类名==cls
    -------------------------------------------------
    我是类方法哈哈呵呵
    打印cls: <class '__main__.Car'>
    打印类名: <class '__main__.Car'>
    

    2.1 验证这句话[哪个类名正在调用这个类方法,cls就是那个类名]

    class Func:
        @classmethod
        def lei(cls):
            print(cls)     #打印cls
    
    Func.lei()        # 调用类方法
    print(Func)       # 打印类名
    -----------------------------------
    <class '__main__.Func'>  # 打印cls
    <class '__main__.Func'>  # 打印类名
    
    

    2.2、类方法使用例子

    # 创建类
    class User:
        # 初始化对象
        def __init__(self, username, password):
            self.username = username
            self.password = password
    
        #  实例方法
        def names(self):
            print(f'my name is {self.username}')
    
        # 类方法
        @classmethod
        def login(cls):
            while 1:
                username = input('请输入用户名:').strip()
                password = input('请输入密码').strip()
                if username == 'alex' and password == '123456':
                    return cls(username, password)  # 创建对象,cls就是类名(相当于User(username,password)
                else:
                    print('用户名或密码错误')
    
    
    u = User.login()  # 通过执行类方法来创建对象,User.login()返回值是 cls(username,password)==User(username,password)
    u.names()         # 通过对象调用实例方法
    ---------------------------------------------
    请输入用户名:alex
    请输入密码123456
    my name is alex
    

    3、静态方法
    通过类名调用
    就是在类里面写的一个函数,必须在上方加上一个@staticmethod装饰器,参数无要求
    一般用于做一些算数逻辑

    class Car:
        age = 23
        def __init__(self, color, name):
            self.color = color
            self.type = name
    
        # 创建实例方法
        @staticmethod
        def run(var):   # 里面可以带参数
            print(f'我是静态方法{var}')
    
    # 调用静态方法 [类名.方法]
    Car.run('haha')
    ----------------------------------------
    我是静态方法haha
    

    4、属性方法
    本质:把属性方法的返回值变成一个实例变量
    访问的时候是通过 【对象.方法】访问就像是在访问实例变量,而不是【对象.方法()】
    用途:实时计算一个东西

    from datetime import datetime
    class Person:
        # 类变量,获取今年年份
        times = int(datetime.now().strftime("%Y"))
        def __init__(self, birth_year):
            self.birth_year = birth_year
            # self.age = Person.times - self.birth_year  #本来应该是这样去计算年龄的
    
        # 属性方法,可以实时去计算年龄
        @property  # 负责把一个方法改成实例变量,把这个方法的返回值变成了self.age
        def age(self):
            print('你今年的年龄是:')
            return  Person.times - self.birth_year  # 类变量-实例变量
    
    
    year = int(input('请输入你的出生年份:'))
    p = Person(year)       # 造对象
    print(p.age)           # 调用属性方法,注意方法名不加括号(就相当于在调用 self.age)
    ---------------------------------------
    请输入你的出生年份:1991
    你今年的年龄是:
    28
    

    4.1、给属性方法重新赋值

    class Person:
        def __init__(self,money):
            self.money = money
            #print(self.money)
    
        # 属性方法
        @property
        def prize(self):
            return '本次累计奖金是:', 20000 + self.money
    
        # 属性方法的分身
        @prize.setter  # 修改上一个方法的返回值,把一个新的值带进去重新计算
        def prize(self, var1):
            self.var1 = var1    # self.var1 把新的变量初始化成实例变量
            print('下次累计奖金是', 20000 + self.var1)
            self.money = 20000 + self.var1   # 还可以把改变的结果重新赋值给实例变量
    
    p = Person(500)    # 创建对象
    print(p.prize)     # 打印第一个prize的返回值
    p.prize = 1000     # 我对第一个prize返回值不满意,所以通过@prize.setter给var1传入一个新的值,去改变prize的结果,再次打印新的prize的返回值
    ----------------------------------------------
    ('本次累计奖金是:', 20500)
    下次累计奖金是 21000
    

    5、在一个类中调用另一个类中的变量

    # 在Person类中调用Func类中的name
    class Func:
        def __init__(self, name):
            self.name = name
    
    class Person:
        def __init__(self,age):
            self.age = age        # self.age 就等于对象 Func('alex')
            
        def test(self):
            print(self.age.name)  # 在Person类中调用Func类中的name, 就等于执行 Func('alex').name
    
    F = Func('alex')    # 造对象,把'alex'给变量name
    P = Person(F)        # 造对象,把Func('alex')这个对象给变量age
    -----------------------------------------
    alex
    

    6、在一个方法中调用另一个方法中的变量
    如果一个类下面的函数都需要去执行某个变量,要么把这个变量写成全局变量,通过global去调用,要么放到init里面,要么就使用下面的方法把这个函数专门当成变量函数
    函数里面的变量只能作用于当前函数中使用,别的函数不能去调用
    一个函数最好只办一件事,不用把所有功能都写到一个函数中,不然会出问题的。

    (1) 写法一
    class Func:
        # 这个函数专门是用来获取age的,里面不要放其他代码
        def test1(self):
            age = input('请输入年龄:')   # 定义全局变量,这个变量无法在外面访问
            self.age = age              # 只能先把全局变量转换成实例变量
    
        def test2(self):
            # self.test1()              # 在这里先执行一个test1也可以
            print(self.age)           # 这边才能够拿到self.age
    
    F = Func()  # 造对象
    F.test1()   # 必须先执行一次test1
    F.test2()
    ---------------------------------------
    请输入年龄:23
    23
    
    (2)写法二
    class Func:
        # 这个函数专门是用来获取age的,里面不要放其他代码
        def test1(self):
            age = input('请输入年龄:')   # 定义全局变量,这个变量无法在外面访问
            self.age = age              # 只能先把全局变量转换成实例变量
            return self.age             # 作为return返回值
    
        def test2(self):
            print(self.test1())        # 执行test1()并打印return返回值
    
    F = Func()  # 造对象
    F.test2()
    -----------------------------------
    请输入年龄:23
    23
    
    
    (3)写法三
    class Func:
        def test1(self):
            self.name = 'alex'
            self.test2()            # 先执行一遍test2()
    
        def test2(self):
            print('哈哈',self.name)  # 可以直接使用test1()里面的变量
    
    F = Func()
    F.test1()     # 调用test1()方法
    ----------------------------------
    哈哈 alex
    

    7、在一个类的方法中调用另一个类中的方法中的变量

    class Func:
        # 实例方法
        def test(self):
            self.age = 111
    
    class Func2(Func):     # 继承
        def test2(self):
            self.test()      # 先把test()方法引用进来
            print(self.age)  # 再去打印test()中的变量
    
    F = Func2()    # 创建Func2()的对象
    F.test2()      # 执行test2()方法
    

    四、私有

    1、私有
    __开头的内容都是私有
    私有内容只能在类里面访问,不允许在类外面访问

    class Person:
        __names = 'Alex'             # 私有类变量
        def __init__(self,name,age):
            self.__name = name        # 初始化成私有实例变量
            self.__age = age
    
        def discrib(self):
            print('my name is %s' %(self.__name))       # 私有实例变量只能通过%s使用
            print(f'My NAME Is {self.__name}')          # 私有实例变量也可以这么用(pycharm会提示,不用管它)
            print(f'my name is %s' %(Person.__names))   # 私有实例变量不能这么用
    
    p = Person('oldboy','23')
    p.discrib()
    print(p__name)    # 这样是不行的
    ----------------------------------
    my name is oldboy
    My NAME Is oldboy
    my name is Alex
    

    五、特殊成员

    1、这里只是稍微简单介绍一下,一般很少能用到
    特殊成员只有在特殊情况下才会被访问. 一般我们很少主动去访问这些内容. 都是自动调用的.
    比如, 我们在创建对象的时候, 会默认执行__init__()方法
    init: 创建对象的时候执行__init__
    new: 创建对象的时候执行__new__
    call: 当执行 对象() 的时候执行__call__

    特殊成员只有在特殊情况下才会被访问. 一般我们很少主动去访问这些内容. 都是自动调用的.
    比如, 我们在创建对象的时候, 会默认执行__init__()方法
    
    class Foo:
        def __init__(self):
            print('默认也会访问我')
    
        def __new__(cls,*args, **kwargs):
            print('默认先会访问我')
            return super().__new__(cls)
    
        def __call__(self, *args, **kwargs):
            print('默认最后访问我')
    
    f = Foo()   # 创建对象时,默认会先执行__new__,后执行__init__
    print('---------------')
    f()   # 把对象当成函数一样去执行,默认会执行到__call__
    

    getitem
    settiem
    delitem

    class Foo:
        def __init__(self):
            self.dic = {}     # 创建一个空字典
    
        # 用于添加
        def __setitem__(self, key, value):
            print('我是setitem')
            print(key, value)
            self.dic[key] = value  # 调用实例变量,把用户输入的key value 写入字典中
    
        # 用于查询
        def __getitem__(self, item):
            print('我是getitem')
            return self.dic.get(item)  # 调用实例变量,去字典中查询item对应的值
    
        # 用于删除
        def __delitem__(self, key):
            print('我是delitem')
            self.dic.pop(key)     # 调用实例变量,删除字典中的key
    
    f = Foo()        # 创建对象
    f["name"] = "alex"  # 调用__setitem__方法,把"name"给key,把"alex"传给value,并写入字典
    print(f["name"])     # 调用__getitem__方法, 把"name" 传给item,并进行查询
    del f['name']       # 调用__delitem__方法删除字典里面的key
    print(f.dic)         # 查看字典中的内容
    ----------------------------------------------
    我是setitem
    name alex
    我是getitem
    alex
    我是delitem
    {}
    

    六、继承

    一、继承
    作用:减少代码量,让父类作为公共代码供子类调用

    1、单继承
    子类可以继承父类中私有内容 (__开头的) 以外的所有内容

    class Animal:                   # 父类
        def cry(self):
            print('我会汪汪叫')
    
    class Cat(Animal):              # 子类继承父类,子类是Cat, 父类是Animal
        def catch_mouse(self):
            print('我会抓老鼠')
    
    p = Cat()  # 创建子类的对象
    p.catch_mouse()
    p.cry()    # 由于子类继承了父类,所以子类对象也可以调用父类里面的方法
    ----------------------------------------------
    我会抓老鼠
    我会汪汪叫
    

    子类中可以有和父类重名的方法,优先会执行子类中的方法

    class Animal:                   # 父类
        def cry(self):
            print('我会汪汪叫')
    
        def like(self):
            print('我最爱吃肉骨头')
    
    class Cat(Animal):              # 子类继承父类,子类是Cat, 父类是Animal
        def catch_mouse(self):
            print('我会抓老鼠')
    
        def cry(self):
            print('我会喵喵叫')
    
    p = Cat()  # 创建子类的对象
    p.catch_mouse()
    p.cry()    # 如果子类里面的方法和父类里面的方法重名,优先执行子类里面的方法
    p.like()
    ---------------------------------
    我会抓老鼠
    我会喵喵叫
    我最爱吃肉骨头
    

    2、多继承

    class Father1:                        # 父类1
        def apple(self):
            print('Father1可以给你买苹果')
    
        def play(self):
            print('Father1可以陪你玩')
    
        def car(self):
            print('Father1开法拉利陪你玩')
    
    class Father2:                        # 父类2
        def orange(self):
            print('Father2可以给你买橘子')
    
        def play(self):
            print('Father2可以陪你玩')
    
        def car(self):
            print('Father2开凯迪拉克陪你玩')
    
    class Son(Father1, Father2):    # 子类继承2个父类,执行顺序:就近原则
        def play(self):
            print('自己和自己玩')
    
    p = Son()     # 造子类对象
    
    p.apple()     # 由于子类继承了父类,所以子类对象可以调用父类中的方法
    p.orange()
    
    p.play()      # 出现同名方法,优先执行子类中的方法
    p.car()       # 父类中出现同名方法,优先执行最近的父类中的方法
    --------------------------------
    Father1可以给你买苹果
    Father2可以给你买橘子
    自己和自己玩
    Father1开法拉利陪你玩
    

    3、super()
    如果子类/父类中的方法执行完,还想继续执行下一个类中的方法就用super()

    class Father1:
        def play1(self):
            print('Father1开飞机陪你玩')
    
        def money(self):
            print('Father1每天给10000块钱')
    
    class Father2:
        def play2(self):
            print('Father2开飞船陪你玩')
    
        def play1(self):
            print('Father2开飞碟陪你玩')
    
        def money(self):
            print('Father2每天给1块钱')
            super().money()   # 继续执行下一个类中的money()
    
    class Son(Father2,Father1):  # 就近原则
        def like(self):
            print('哈哈哈')
            super().play2() # 执行完子类中的方法后继续执行下一个类中的play2()方法
            super().play1() # 执行完子类中的方法后继续执行下一个类中的play1()方法
            # super(Son, self).play1()  # 也可以写成这样,直接tab补全就行,意思是执行Son后面的类中的方法
    
    
    p = Son()  # 造子类对象
    p.like()   # 先找子类中的方法并执行
    p.money()  # 如果子类中没有这个方法就执行父类中的方法,先去Father2中找
    print(Son.mro())  # 如果继承关系很复杂可以通过mor() 查看优先执行顺序
    ----------------------------------------------------
    哈哈哈
    Father2开飞船陪你玩
    Father2开飞碟陪你玩
    Father2每天给1块钱
    Father1每天给10000块钱
    [<class '__main__.Son'>, <class '__main__.Father2'>, <class '__main__.Father1'>, <class 'object'>]
    

    4、super()的继承原理

    class Father:
        def __init__(self, address):  # (4) 这里的self == s对象,并初始化s对象中的address
            self.address = address
    
    class Son(Father):
        def __init__(self, address):  # (2)初始化对象,self==s对象
            super(Son, self).__init__(address)   # (3)执行父类中的__init__,这里的self是子类中的self传过来的==s对象,并把address传给父类
            #super().__init__(address) # 或写成这样
    
    
    s = Son('北京')  # (1)创建对象
    print(s.address)
    ----------------------------------------------
    北京
    

    七、反射

    一、反射(就是方便在某一个对象中去寻找某一个功能并执行)
    getattr 可以从模块、类、对象中获取功能,在python中一切皆对象,所以说getattr可以从对象中获取功能
    getattr(对象, 功能):在某一个模块中去寻找某个功能 (get表示找,attr表示功能)
    hasattr(对象,功能):判断模块中是否有某个功能
    callable() 判断某个功能能否被调用(只有函数、类、对象能被调用)

    1、定义一个模块mode12.py

    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    # 这是创建的模块mode12
    
    name = 'alex'
    
    # 功能
    def play():
        print('正在玩')
    
    def run():
        print('正在跑')
    
    def eat():
        print('正在吃')
    

    2、getattr() 用法

    import mode12                # 引入定义的模块
    fn = getattr(mode12, "run")  # 在mode12模块中找run这个功能(注意用引号引起来),并赋值给fn
    fn()                         # 找到后就去执行这个功能
    -----------------------------------------------------
    正在跑
    

    3、hasattr() 用法

    (1) 如果mode12模块中有run功能,则返回True,否则返回False
    import mode12
    print(hasattr(mode12, "run"))
    ------------------------------
    True
    
    (2) hasattr 结合 getattr 使用
    import mode12
    if hasattr(mode12, "run"):       # 判断mode12模块中是否有这个功能
        fn = getattr(mode12, "run")  # 如果有这个功能,就获取这个功能
        fn()                          # 并执行这个功能
    else:
        print('没有找到这个功能')
    

    4、callable()用法

    import mode12
    gn = input('请输入查找的功能:')
    # 判断mode12模块中是否有这个功能,这里的功能需要用""引起来
    if hasattr(mode12, gn):
        # 如果有这个功能,就获取这个功能
        fn = getattr(mode12, gn)
        # 判断这个功能是否能被调用(函数、类、对象都可以被调用)
        if callable(fn):
            fn()                          # 能被调用就执行这个功能
        else:
            print(fn)                     # 不能被调用就打印这个功能(比如变量)
    else:
        print('没有找到这个功能')
    ------------------------------------
    请输入查找的功能:run
    正在跑
    

    5、用getattr() 从类中获取功能

    class Func:
        name = 'alex'
        # 功能
        def play(self):
            print('正在玩')
    
        @classmethod
        def run(cls):
            print('正在跑')
    
    # 调用类里面的实例方法
    fn = getattr(Func, 'play')
    print(hasattr(Func, 'play'))
    fn('')         # 里面必须加点东西
    
    # 调用类里面的类方法
    fc = getattr(Func, 'run')
    fc()         # 里面不用加东西
    ---------------------------------------
    True
    正在玩
    正在跑
    

    6、用getarrt() 从对象中获取内容

    class Func:
        name = 'alex'
        # 功能
        def play(self):
            print('正在玩')
    
        @classmethod
        def run(cls):
            print('正在跑')
    
    # 造对象
    F = Func()
    
    # 从对象中获取实例方法
    print(hasattr(F, 'play'))
    fn = getattr(F, 'play')
    fn()      # 这里相当于F.play()
    
    # 从对象中获取类方法
    fn = getattr(F, 'run')
    fn()      # 这里相当于F.run()
    
    # 给对象创建一个新的变量,内容是'girl' (创建在内存里面)
    setattr(F, 'hobby', 'girl')           # setattr(对象,方法, 内容),用的少
    print(F.hobby)
    --------------------------------------
    True
    正在玩
    正在跑
    girl
    

    八、约束

    一、约束
    子类继承父类后,必须让子类重新某个方法
    1、原理

    class Father:
        def sleep(self):
            raise NotImplementedError('你需要自己创建这个方法')
    
    class Son(Father):      # 子类继承父类
        pass
    
    # 如果子类想调用父类中的sleep()方法,就会报错,必须自己去创建sleep()方法
    S = Son()
    S.sleep()   
    ------------------------------
    NotImplementedError: 你需要自己创建这个方法
    

    九、三元表达式

    1、三元表达式
    条件成立返回a,否则返回b

    # 返回最大的值
    a = 1
    b = 2
    c = a if a>b else b  # 如果a>b就返回a,否则返回b
    print(c)
    -----------------------------
    2
    

    作者:会飞的爱迪生

    物联沃分享整理
    物联沃-IOTWORD物联网 » Python面向对象编程深度解析

    发表回复