三、Python面向对象(二)

面向对象编程三大特性

封装

  • 私有属性
在属性名前面加两个连续的下划线, 这个属性就变成了私有属性
私有属性只能在类的内部使用, 不能在类的外部使用
'''
定义一个类, Person
有属性 姓名name, 体重weight
有方法run, 调用一次run方法weight少0.5公斤
有方法eat, 调用一次eat方法weight多1公斤
有方法show, 作用就是介绍自己, 其实就是显示name和weight的值
'''
class Person:
    def __init__(self, name, weight):
        self.name = name
        self.weight = weight

    def run(self):
        self.weight -= 0.5

    def eat(self):
        self.weight += 1

    def show(self):
        print(f"我的名字叫{self.name}, 我的体重为{self.weight}")

xiaoming = Person("小明", 70) # 实例化小明对象, 体重是70公斤
xiaoming.show()
xiaoming.run()
xiaoming.run()
xiaoming.run()
xiaoming.show()
xiaoming.eat()
xiaoming.show()
xiaomei = Person("小美", 50)
xiaomei.show()
xiaomei.eat()
xiaomei.eat()
xiaomei.eat()
xiaomei.eat()
xiaomei.eat()
xiaomei.weight = 45  # 在类 的外部修改了属性的值
xiaomei.show()
  • 通过私有属性封装weight的案例
'''
定义一个类, Person
有属性 姓名name, 体重weight
有方法run, 调用一次run方法weight少0.5公斤
有方法eat, 调用一次eat方法weight多1公斤
有方法show, 作用就是介绍自己, 其实就是显示name和weight的值
'''
class Person:
    def __init__(self, name, weight):
        self.name = name
        self.__weight = weight  # __weight属性是私有的, 只能在类的内部使用

    def run(self):
        self.__weight -= 0.5

    def eat(self):
        self.__weight += 1

    def show(self):
        print(f"我的名字叫{self.name}, 我的体重为{self.__weight}")

xiaoming = Person("小明", 70) # 实例化小明对象, 体重是70公斤
xiaoming.show()
xiaoming.run()
xiaoming.run()
xiaoming.run()
xiaoming.show()
xiaoming.eat()
xiaoming.show()
xiaomei = Person("小美", 50)
xiaomei.show()
xiaomei.eat()
xiaomei.eat()
xiaomei.eat()
xiaomei.eat()
xiaomei.eat()
xiaomei.__weight = 45  # 在类 的外部修改了属性的值, __weight是私有属性, 在类的外部修改无效
xiaomei.show()
  • 私有方法, 在方法名前面加两个连续的下划线, 方法就成为了私有方法
    • 私有方法只能在类的内部调用, 不能在类的外部调动
class Person:
    def __secret(self): # __secret为私有方法, 只能在类的内部调用
        print("我的秘密是, 睡觉磨牙打呼噜")

    # 只有正确的输入了密码后, 才能看到小秘密
    def show(self, password):
        if password == "123456":
            self.__secret() # 调用私有方法
        else:
            print("密码不对, 不告诉你我的小秘密哦")


p = Person()
# p.__secret() # 不能在类的外部调用私有方法__secret

p.show(input("请输入密码"))

继承

  • 子类会继承父类的方法和属性
class 父类:
    pass

class 子类(父类):
    pass
class Animal: # 有一个动物类
    def eat(self):
        print("吃")

    def sleep(self):
        print("睡")

class Dog(Animal): # Dog是子类, 继承自父类Animal
    def run(self):
        print("跑")

a = Animal()
a.sleep()
a.eat()  # Animal类只有sleep和eat方法

d = Dog()
d.eat() # 继承自父类
d.sleep()  # 继承自父类
d.run()  # run是Dog自己的
# Dog类有eat, sleep和run三个方法
  • 一个父类可以有多个子类
class Animal: # 有一个动物类
    def eat(self):
        print("吃")

    def sleep(self):
        print("睡")

class Dog(Animal): # Dog是子类, 继承自父类Animal
    def run(self):
        print("跑")

class Fish(Animal):
    def swimming(self):
        print("游水")

class Bird(Animal):
    def fly(self):
        print("飞")

'''
Animal类有两个方法, eat和sleep
Dog类有三个方法, eat和sleep还有run
Fish类有三个方法, eat和sleep还有swimming
Bird类有三个方法, eat和sleep还有fly
'''
a = Animal()
a.sleep()
a.eat()
d = Dog()
d.sleep()
d.eat()
d.run()
f = Fish()
f.sleep()
f.eat()
f.swimming()
b = Bird()
b.sleep()
b.eat()
b.fly()
  • 多级继承
class Animal: # 有一个动物类
    def eat(self):
        print("吃")

    def sleep(self):
        print("睡")

class Dog(Animal): # Dog是子类, 继承自父类Animal
    def run(self):
        print("跑")

class ErHa(Dog):
    def chaijia(self):
        print("拆家")

class LangGou(Dog):
    def yaoren(self):
        print("咬人")

'''
类ErHa有eat, sleep, run和chaijia
类LangGou有有eat, sleep, run和yaoren
'''
e = ErHa()
e.sleep()  # 来自于爷爷
e.eat()  # 来自于爷爷
e.run() # 来自于父亲
e.chaijia()  # 自己定义的方法
l = LangGou()
l.sleep()
l.eat()
l.run()
l.yaoren()

Object类

  • 在python3以上的版本中, 如果一个类没有明确的指定父类, 这个类继承自Object类
class A:
    pass
# 类A继承自Object类
# python所有的类, 都是Object的子类

方法的重写

  • 当父类的方法不能满足子类的需求, 那么子类就要修改方法

覆盖

  • 子类把父类的同名方法覆盖
  • 子类中如果出现和父类同名的方法, 那么在子类中父类的同名方法就被覆盖了
class Animal: # 有一个动物类
    def eat(self):
        print("吃")

    def sleep(self):
        print("睡")

class Dog(Animal): # Dog是子类, 继承自父类Animal
    def eat(self):  # 如果子类出现了和父类同名的方法, 那么在子类中, 父类的方法就不存在了
        print("吃肉")

a = Animal()
a.eat() # Animal的eat没有任何改变

d = Dog()
d.sleep() # 这个是来自于父类Animal
d.eat()  # 这个是来自于自己

扩展super()

  • 子类在父类方法上添加功能
    • super().父类的同名方法
class Animal: # 有一个动物类
    def eat(self):
        print("吃")

class Dog(Animal): # Dog是子类, 继承自父类Animal
    def eat(self):  # 如果子类出现了和父类同名的方法, 那么在子类中, 父类的方法就不存在了
        super().eat()  # 调用父类的eat方法
        print("dog类自己的eat方法")

d = Dog()
d.eat()

属性的继承

  • 父类的属性会继承给子类
class Animal: # 有一个动物类
    def __init__(self, sex, age):
        self.sex = sex
        self.age = age

class Dog(Animal):  # 由于父类Animal定义了__init__, 所以子类会自动继承这个父类的__init__
    def show(self): # 显示属性sex和age
        print(f"sex属性的值为{self.sex}, age属性的值为{self.age}")

# d = Dog() # 实例化的时候, 不提供__init__的实参, 语法会报错
d = Dog("男", 10)
d.show()
# 在Dog中由于自动继承了父类的__init__方法, 所以父类中定义的属性sex和age也继承给了子类
  • 父类和子类都有自己的属性
class Animal: # 有一个动物类, 有属性sex和age
    def __init__(self, sex, age):
        self.sex = sex
        self.age = age

# 子类Dog不但要继承父类的属性, 还有自己特有的属性name
class Dog(Animal):  # 由于父类Animal定义了__init__, 所以子类会自动继承这个父类的__init__
    # Dog类有三个属性, sex和age来自于Animal, name来自于自己
    def __init__(self, sex, age, name): # 这里的三个形参的作用是给三个属性提供初值
        self.name = name
        # 由于Dog类也有__init__, 所以会把Animal类的__init__覆盖
        super().__init__(sex, age) # 调用父类的__init__

    def show(self): # 显示属性sex和age
        print(f"name属性的值为{self.name}, sex属性的值为{self.sex}, age属性的值为{self.age}")


d = Dog("男", 10, "tom")
d.show()
'''
类Animal有属性age
类Dog继承自Animal,有属性name
类Dog有方法show, 显示属性age和name
'''
class Animal:
    def __init__(self, age):
        self.age = age

class Dog(Animal):
    def __init__(self, name, age): # 由于Dog有两个属性name和age, 所以__init__提供了两个对应的形参
        self.name = name
        super().__init__(age)

    def show(self):
        print(f"我的名字叫{self.name}, 我的年龄是{self.age}")

d = Dog("小狗", 3)
d.show()

image

父类的私有成员子类不能继承

  • 父类中带有连续两个下划线的方法和属性都是私有的, 私有的成员不能继承给子类
class Animal:
    def __init__(self):
        self.__name = "动物"  # __name是私有属性

    def __my_func(self):  # 私有方法
        pass

class Dog(Animal):
    pass

d = Dog()
# d.__my_func()  # 语法报错, 错误的原因是父类的方法是私有的, 不会继承给Dog类
# print(d.__name)  # 语法报错, 原因是父类的私有属性,不能继承给子类

多态

  • 不同的子类对象, 调用相同的父类方法, 产生不同的执行结果
类A有方法show
类B1和B2都是继承自类A
B1的对象调用show方法
B2的对象调用show方法
两个对象调用的都是父类的show方法, 但执行的结果不一样
'''
class A:
    def show(self):
        print("show")

class B1(A):
    pass

class B2(A):
    pass

b1 = B1()
b1.show()
b2 = B2()
b2.show()
# 上面的代码不同的子类对象, 调用相同的父类方法, 执行结果也是一样, 这个不是多态
'''
class A:
    def show(self):
        self.my_func()

    def my_func(self):
        print("我是a")

class B1(A):
    def my_func(self):
        print("我是b1")

class B2(A):
    def my_func(self):
        print("我是b2")

b1 = B1()
b1.show()  # 内部代码一定是调用了B1 的my_func方法
b2 = B2()
b2.show() # 内部代码一定是调用了B2 的my_func方法

类属性

  • 不需要类的实例, 直接可以使用的属性
  • 普通属性(实例属性)一定要在方法内部定义, 定义的时候self.属性名 = 值
  • 类属性定义在类的内部, 但是在方法的外部, 定义的时候不需要使用self
  • 类属性直接用类名.类属性名 使用
# 类A的name是普通属性, 所以一定要实例化为对象后, 通过对象使用
class A:
    def __init__(self, name):
        self.name = name

a = A("tom")
print(a.name) # 使用对象a的属性name
# 普通属性, 也叫实例属性, 这个属性必须通过类的实例来使用

# 类B的name是类属性, 所以不需要实例, 直接可以通过类名使用
class B:
    name = "tom"   # 定义了一个类属性name, 值为"tom"
    def __init__(self):
        pass

print(B.name)  # 使用B类的类属性name

类方法

  • 普通方法也叫实例方法,, 只能通过实例调用的方法
  • 不需要实例, 直接通过类名.类方法名就能调用的方法
class 类名:
    @classmethod  # 定义类方法上面必须写
    def 类方法(cls, 形参1, 形参2......): # 类方法的第一个形参必须是cls
        pass
class A:
    @classmethod
    def my_func(cls):
        print("我是类方法my_func")

A.my_func()

类方法的形参cls的作用

cls的作用一

  • 在类方法中使用类属性
class A:
    age = 0  # 这里定义的类属性age
    @classmethod
    def show_age(cls):
        # 在类方法内部显示类属性的值
        print(cls.age)  # 在类方法内部使用类属性

    @classmethod
    def set_age(cls, age): # age是形参
        # 在类方法内部修改类属性的值
        cls.age = age  # 把形参age的值给类属性age


A.set_age(200)
A.show_age() # 调用类方法

cls的作用二

  • 在一个类方法中调用另一个类方法
class A:
    @classmethod
    def my_func1(cls):
        print("my_func1")

    @classmethod
    def my_func2(cls):
        # 在这个类方法中调用my_func1类方法
        cls.my_func1()

A.my_func2()
  • 类方法使用场景
    • 创建一个只能有一个实例的类(单例)
class A:
    __tmp = None  # __tmp是当做类的A对象来使用, 但代码在这里还不确定, 所以给个None
    @classmethod
    def create(cls):  # 只要调用类方法create就会生成类A的实例, 不管调用多少次, 但实例只有一个
        if cls.__tmp is None:
            cls.__tmp = A()  # 如果条件成立, 执行实例化的过程
            return cls.__tmp
        else:
            return cls.__tmp

    def __init__(self): # 为了证明这个类到底被实例化了多少次
        print("类A被实例化了, 因为实例化的时候会自动调用__init__")

# 类属性tmp的值是None, 所以if cls.tmp is None这个条件成立, 所以执行了实例化的语句cls.tmp = A()
a = A.create()
# 类属性tmp的值已经是一个对象了, 不是None, if cls.tmp is None条件不成立, 没有执行实例化的代码
b = A.create()
# 类属性tmp的值已经是一个对象了, 不是None, if cls.tmp is None条件不成立, 没有执行实例化的代码
c = A.create()
# 不管调用多少次create, 但cls.__tmp = A()语句只执行过一次

静态方法

  • 不需要实例, 直接可以通过类名.静态方法名 调用的方法
class 类名:
    @staticmethod
    def 静态方法名(形参1, 形参2........):
        pass
class A:
    @staticmethod
    def help():  # 定义静态方法help
        print("我是静态方法help")

A.help() # 调用静态方法help
  • 使用场景
    • 如果一个项目, 有很多函数, 函数多了, 名字会重名, 可以把一些和类确实无关, 但有担心重名的函数, 做为类的静态方法使用
    • 目的是避免因为函数重名带来的冲突
class A:
    @staticmethod
    def my_func():
        print("aaaaaaaaaaaaaaa")


class B:
    @staticmethod
    def my_func():
        print("bbbbbbbbbbbbbbbbbb")


A.my_func() # 本意是想调用第一个my_func

总结

image

posted @ 2022-05-24 21:33  鬼谷仙生  阅读(97)  评论(0编辑  收藏  举报