python面向对象3大特性:封装、继承、多态(依赖注入)

基础复习

class Animal:
    def __init__(self,name):
        self.name=name
    def info(self):
        print(f"{self.name} is an animal")
    def update_name(self,name):
        self.name=name+'**'
dog=Animal('来福')#实例化
print(dog.name)#1、访问属性
dog.info()#2、访问方法
dog.name='旺财'#3-1、直接修改属性
dog.info()
dog.update_name('叮当')#3-2、通过方法修改属性值
dog.info()

1、封装:封装可以隐藏实现细节,保护隐私、使得代码模块化

面向对象封装是一种编程概念,它将数据和操作数据的方法封装在一个对象中,通过对象的接口进行访问和操作,同时隐藏了对象内部的实现细节。

封装的目的是将数据和方法组织在一起,形成一个独立的、可重用的模块,通过对象的方法来操作数据,而不是直接访问和修改数据。

封装有以下几个主要的特点和优势:

  1. 数据隐藏:封装可以隐藏对象内部的数据和实现细节,只暴露必要的接口方法给外部使用。这样可以保护数据的完整性,防止直接对数据进行非法或无效的访问和修改。

  2. 信息隐藏:封装可以隐藏对象的内部实现细节,只暴露对外的接口。这样可以提高代码的可维护性,内部的变化和修改不会影响外部的调用代码。

  3. 代码复用:封装可以将相关的数据和方法组织在一个对象中,形成一个独立的模块,可以在不同的地方进行重用,提高代码的可复用性和模块化程度。

  4. 接口定义:封装通过对象的方法提供了明确定义的接口,规范了对象的使用方式。使用者只需了解对象的接口方法,而不需要了解对象的内部实现细节,降低了使用的复杂性。

1、数据封装:保护隐私(私有化)
2、方法封装:隔离复杂度
子类无法覆盖父类的隐藏属性
A、
class Person:
    country='China'
    def __init__(self,name,age):
        self.name=name
        self.age=age
    def get_info(self):
        print(f"姓名{self.name},年龄{self.age}")
----------------------------------------
Jack=Person('jack',20)
print(Person.country,Jack.name,Jack.age,Jack.get_info)#类名.属性  实例名.属性  实例名.方法

B、私有属性和方法、@property
私有化类属性:类内部方法调用它
class Goods:
    __discount = 0.8
    def __init__(self,name,price,weight='kg'):
        self.name = name
        self.price = price
        self.__weight =weight
    def goods_price(self):
        return  self.price * Goods.__discount
    def tel_weight(self):
        return self.__weight
    def __newname(self):
        return self.name+'**'
apple = Goods('apple',10,'kg')
print(apple.goods_price())
print(apple.tel_weight())
print(apple._Goods__newname())
# print(Goods._Goods__discount)
# print(apple._Goods__weight)
=============================================================================
C、
from math import pi
class Circle:
    '''@property 统一属性调用且不可修改'''
    def __init__(self,radius):
        self.radius = radius
    @property
    def area(self):
        '''圆面积'''
        return self.radius * self.radius* pi
    @property
    def peimeter(self):
        '''圆周长'''
        return 2*pi*self.radius
c = Circle(10)
print(c.area)#@property 装饰器:把一个方法当成一个属性用了,就不用加括号了
print(c.peimeter)


class Goods:
    __discount = 0.8
    def __init__(self,name,price):
        self.name = name
        self.__price = price
    @property
    def price(self):
        return self.__price * Goods.__discount

    @price.setter
    def price(self,new_price):
        if type(new_price) is int:
            self.__price = new_price

    @price.deleter
    def price(self):
        del self.__price

    def set_price(self):
        return self.__price*Goods.__discount
apple = Goods('apple',10)
print(apple.price)
apple.price = 10#new_price 修改属性
print(apple.price)

# del apple.price
# print(apple.price)
print(apple._Goods__price)
apple._Goods__price=20
print(apple.set_price())
=============================================================================
D、
class Foo:
    def __func(self):
        print('from foo')
class Bar(Foo):
    def __func(self):
        print('from bar')
b = Bar()
b._Foo__func()
b._Bar__func()
==============================================================================
class Login:
    def login(self,user,pwd):
        '''实现登录'''
        pass

from login import Login
lg=Login()
lg.login('admin','123456')#登录功能封装后,后续操作只要调用登录模块就可以了。

2、继承

=================单继承、重写、给子类定义属性/方法=====
class Car():
    def __init__(self,make,model,year):
        '''父类'''
        self.make=make #品牌
        self.model=model #型号
        self.year=year #出厂年份
        self.odometer_reading=0 #初始里程
    def get_info(self):
        return self.make+'_'+self.model+'_'+str(self.year)
    def increase_odometer(self,miles):
        self.odometer_reading+=miles
    def fill_gas_tank(self):
        print("加油完毕")
class ElectricCar(Car):
    '''子类'''
    def __init__(self,make,model,year,battery_size=70):
        '''1、子类的init方法初始化 父类的属性'''
        super().__init__(make,model,year)#调用父类的init方法
        self.battery_size=battery_size#2、给子类定义属性
    def get_battery_info(self):#3、给子类定义方法
        print("This car has a"+str(self.battery_size)+"-kwh battery.")
    def fill_gas_tank(self):
        print("电动车没有油箱")#4、重写父类方法###################
myElectricCar=ElectricCar('tesla','model_s',2016)
print(myElectricCar.get_info())
myElectricCar.get_battery_info()
myElectricCar.fill_gas_tank()
#调用父类中被重写的方法!!
Car.fill_gas_tank(myElectricCar)##################通过父类名调用
=============================将实例用作属性=====================================
class Car():
    def __init__(self,make,model,year):
        '''父类'''
        self.make=make #品牌
        self.model=model #型号
        self.year=year #出厂年份
        self.odometer_reading=0 #初始里程
    def get_info(self):
        return self.make+'_'+self.model+'_'+str(self.year)
    def increase_odometer(self,miles):
        self.odometer_reading+=miles
    def fill_gas_tank(self):
        print("加油完毕")
class Battery():
    def __init__(self,battery_size):
        self.battery_size = battery_size
    def get_battery_info(self):
        print("This car has " + str(self.battery_size) + "-kwh battery.")

class ElectricCar(Car):
    '''子类'''
    def __init__(self,make,model,year,battery_size):
        super().__init__(make,model,year)#super函数调用 父类构造方法!!只能在class里用
        #super() 参数1=所在类(从哪开始找),
                #参数2=self=object 参数1开始找第一个init函数 Car的init函数第一个参数self
        #super(ElectricCar,self).__init__(make,model,year)#等价 完全体 class内外用
        self.battery=Battery(battery_size)#将实例用作属性

myElectricCar=ElectricCar('tesla','model_s',2016,88)
print(myElectricCar.get_info())
myElectricCar.battery.get_battery_info()
---------------------super()类-- 解决多继承mro查找问题(理解不够?)---------------------
官方解释:用于调用相同的父类方法。
调用父类构造方法。
1、父类有构造函数,子类构造方法 用super()初始化父类属性。
class Animal:
    def __init__(self,age):
        self.age=age
class Person(Animal):
    def __init__(self,age,name):
        super().__init__(age)#参数1:所在class,表示从哪开始找依据mro 参数2:self=object 第一个class的init函数依据mro
        # super(Person,self).__init__(age)#从Person开始找Animal的init函数,对Person这个object做初始化
        self.name=name
class Male(Person):
    def __init__(self,age,name):
        super().__init__(age,name)
        # super(Male,self).__init__(name,age)
        self.gender='male'
m=Animal(20)
print(m.age)
p=Person('wangcai',21)
print(p.name,p.age)
female=Male('zhaotaohua',22)
print(female.name,female.age,female.gender)

#super(Person,p).__init__(18)
#super(Male,female).__init__('laifu',20)
---------------------------------------------------------------
2、父类有构造函数,子类没有:创建子类对象要调用父类的构造函数
class Dog:
    '''父类有构造函数,子类没有:创建子类对象要调用父类的构造函数'''
    def __init__(self, name):
        self.name = name
    def getName(self):
        return self.name
class Husky(Dog):
    def describe(self):
        print("我们是一群破坏分子")
my_husky = Husky("林林")
print(my_husky.getName())
-----------------------------------------------------------------
3、父类有构造函数,实例化父类实例变量。 1的变种。
class Dog:
    def __init__(self, name):
        self.name = name
    def getName(self):
        return self.name

class Pet:
    def __init__(self, age, sex):
        self.age = age
        self.sex = sex
    def getage(self):
        return self.age

class Husky(Dog, Pet):
    '''子类中定义构造函数,为了初始化父类中定义的实例变量,可以直接调用实例变量进行赋值'''
    def __init__(self, name, age, sex):
        self.name = name
        self.age = age
        self.sex = sex

class Husky2(Dog,Pet):
    '''子类中定义构造函数,为了初始化父类中定义的实例变量,也可以通过调用父类的构造函数初始化:
    1、使用父类名调用父类的构造函数(需要手动指定调用者)
    2、super()
    '''
    def __init__(self,name,age,sex):
        super().__init__(name)
        Pet.__init__(self, age, sex)
my_husky = Husky("林林", 5, "boy")
print(my_husky.getage())
my_husky2 = Husky2("林林", 5, "boy")
print(my_husky2.getage())

======================多继承 顺序================python3广度优先mro(比较复杂!!!)==
例子1:
class A():
    def foo(self):
        print("A  foo")
class B():
    def foo(self):
        print("B  foo")
    def bar(self):
        print("B bar")
class C(A,B):
    pass
class D(A,B):
    def bar(self):
        print("D bar")
class E(C,D):
    pass
# C().foo()#A foo 调用近的A. python3广度优先,先找的A
# C().bar() #B bar
# D().foo()#A foo同上
# D().bar()#D bar
E().foo()#A foo E>C>D>A
E().bar()#D bar E>C>D(B深度、D广度,D优先)

print(E.__mro__)#查询顺序E>C>D>A>B

广度计算不是表面那么简单,借助mro查看
比如X,Y,Z
   A(X,Y) B(Y,Z)
   M(A,B,Z)
执行顺序是:M>A>X>B>Y>Z

==========================子类 资源访问权限=======================
私有属性和方法不能继承使用
class Animal:
    a = 1
    _b = 2
    __c = 3
    def  __init__(self):
        print("Animal内置初始化")
    def aa(self):
        print("aa")
    def _bb(self):
        print("bb")
    def __cc(self):
        print("cc")

class Dog(Animal):
    def test(self):
        print(self.a)
        print(self._b)
        # print(self.__c) #不能访问
        self.aa()
        self._bb()
        # self.__cc()  #不能访问

d = Dog()
d.test()
print(d.a,d._b)
d.aa()
d._bb()

3、多态

用一方法spark在不同派生类Dog Cat中表现不同
class Animal:
    def __init__(self,name):
        self.name=name
    def info(self):
        print("i am an animal")
class Dog(Animal):
    def spark(self):
        print("汪汪汪")
class Cat(Animal):
    def spark(self):
        print("喵喵喵")

dog=Dog('来福')
dog.spark()

cat=Cat('叮当')
cat.spark()

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>依赖注入/控制反转:解耦(平时用的少)动态的把某种依赖注入到对象中
常用依赖注入途径有:接口注入、Set注入、构造注入  反射。
依赖注入的目标是通过将依赖项从一个对象传递到另一个对象,降低它们之间的耦合度。

1、接口注入Interface Injection:继承类的方法实现 父方法
入参不一样,行为不一样
接口注入的核心思想是,一个对象不依赖于具体的实现类,而是依赖于接口或抽象类。通过将接口对象作为参数传递给对象的构造函数或方法,可以在运行时决定实际的实现类,并将其注入到对象中。

演示:
class DatabaseInterface:
    def connect(self):
        pass

    def execute_query(self, query):
        pass

class MySQLDatabase(DatabaseInterface):
    def connect(self):
        print("Connecting to MySQL database")

    def execute_query(self, query):
        print("Executing query in MySQL database:", query)

class PostgreSQLDatabase(DatabaseInterface):
    def connect(self):
        print("Connecting to PostgreSQL database")

    def execute_query(self, query):
        print("Executing query in PostgreSQL database:", query)

class DataProcessor:
    def __init__(self, database):
        self.database = database

    def process_data(self, data):
        self.database.connect()
        # 处理数据并执行查询等操作
        print("Processing data using", type(self.database).__name__)

# 创建具体的数据库实现类对象
mysql_db = MySQLDatabase()#【对象不同,行为不同】
postgres_db = PostgreSQLDatabase()

# 创建数据处理器对象并注入不同的数据库实现类
data_processor1 = DataProcessor(mysql_db)#【对象不同,行为不同】
data_processor2 = DataProcessor(postgres_db)

# 使用不同的数据库实现类进行数据处理
data_processor1.process_data("Data 1")  # 输出: Connecting to MySQL database, Processing data using MySQLDatabase
data_processor2.process_data("Data 2")  # 输出: Connecting to PostgreSQL database, Processing data using PostgreSQLDatabase

在上面的示例中,定义了一个DatabaseInterface接口,它包含了连接数据库和执行查询的方法。
MySQLDatabase和PostgreSQLDatabase是具体的数据库实现类,它们分别实现了DatabaseInterface接口中的方法。
DataProcessor类是一个数据处理器类,它依赖于DatabaseInterface接口。通过在构造函数中接收DatabaseInterface对象作为参数,并将其赋值给self.database,实现了接口注入。
在使用DataProcessor类进行数据处理时,可以根据需要传入不同的数据库实现类对象,实现了与具体数据库的解耦。这样,在运行时可以灵活地切换和替换不同的数据库实现类。
接口注入可以提高代码的可测试性,例如,在单元测试中可以轻松地传入模拟或虚拟的接口对象来进行测试。同时,它也符合依赖倒置原则,使系统的组件更加可复用、可扩展和可维护。

2、Set注入:类本身的一个方法来注入不同类型的对象。

通过Set注入,我们可以在运行时灵活地替换依赖对象。
class Dependency:
    def do_something(self):
        print("Doing something in Dependency")

class MyClass:
    """Set 注入:类MyClass的方法setObject:注入不同的对象"""
    def __init__(self):
        self.object = None

    def setObject(self, testObject):
        '''方法注入不同的对象'''
        self.object = testObject

    def perform_operation(self):
        if self.object is not None:
            self.object.do_something()

# 创建依赖对象
dependency1 = Dependency()#testObject
# 创建 MyClass 对象并注入依赖对象
my_object = MyClass()
my_object.setObject(dependency1)######## 定义 #########
# 调用 MyClass 的方法,依赖对象会被使用
my_object.perform_operation()  # 输出: Doing something in Dependency

3、构造注入:通过将依赖对象作为构造函数的参数进行注入。
在创建类时间里时为构造方法传入不同对象
class DatabaseConnection:
    def __init__(self, host, port, username, password):
        self.host = host
        self.port = port
        self.username = username
        self.password = password

    def connect(self):
        print("Connecting to database:", self.host, self.port)
        # 执行数据库连接的逻辑

class DataProcessor:
    def __init__(self, database_connection):
        #***2和3区别:通过构造方法实现依赖注入***
        self.database_connection = database_connection

    def process_data(self, data):
        self.database_connection.connect()
        # 处理数据的逻辑

# 创建依赖对象
connection = DatabaseConnection("localhost", 3306, "user", "password")

# 创建 DataProcessor 对象并进行构造注入
data_processor = DataProcessor(connection)#为构造方法传入不同类型的对象

*4、反射:根据传入信息创建不同类型的对象
class Dependency:
    def __init__(self, value):
        self.value = value

class MyClass:
    def __init__(self, dependency):
        self.dependency = dependency

    def do_something(self):
        print(self.dependency.value)

# 通过反射动态创建依赖对象
dependency = Dependency(42)
# 通过依赖注入将依赖对象传递给 MyClass
my_class = MyClass(dependency)#依赖对象注入
# 调用 MyClass 的方法
my_class.do_something()

其他:面向对象经典题目:装饰房子、士兵突击、老师学生类

https://www.cnblogs.com/duanminkid/p/16329525.html
1、装饰房子
'''
需求:
1、房子HOUSE有户型type、总面积area和家具名称列表item=[]
新房子没有任何家具
2、家具HouseItem有名字name和占地面积area,其中
席梦思(bed)占地4平米
衣柜(chest)占地2平米
餐桌(table)占地1.5平米 打印家具信息
3、将以上散件家具添加到房子中 House类
4、打印房子时,要求输出:户型、总面积、剩余面积free_area、家具名称列表
'''
class HouseItem:
    def __init__(self,name,area):
        self.name=name
        self.area=area
    def __str__(self):
        # return "家具[%s],占地%.2f"%(self.name,self.area)
        return f"家具[{self.name}],占地[{self.area}]"
class House:
    def __init__(self,type,area):
        self.type=type
        self.area=area
        self.free_area=area
        self.item=[]
    def add_item(self,houseitem):
        '''houseitem 实例化对象 <__main__.HouseItem object at 0x000002008BF93828>
           houseitem.name,houseitem.area 实例化属性
        '''
        if self.free_area<houseitem.area:
            print("面积太大,无法添加[%s]"%houseitem.name)
            return
        self.item.append(houseitem.name)
        self.free_area = self.free_area - houseitem.area
    def __str__(self):
        return "户型:%s、总面积:%s、剩余面积:%s、家具名称列表:%s"%(self.type,self.area,self.free_area,self.item)
bed=HouseItem('席梦思',4)
chest=HouseItem('衣柜',1)
table=HouseItem('餐桌',1.5)
print(bed,chest,table)

home = House("两室一厅", 6)
print(home)
home.add_item(bed)#############一个类的属性可以是另一个类的对象object####################1
home.add_item(chest)
home.add_item(table)
print(home)
=======================================================================
2、士兵突击
'''
需求:
>枪
1、一把AK47,初始子弹=0
2、枪可以加载子弹 -- 增加子弹数量
3、枪可以开火--子弹-1
>士兵:
1、初始枪=None
2、士兵冲锋陷阵:无枪 口号return
             有枪,加载子弹,射击完全部子弹
'''
class Gun:
    def __init__(self,model):
        self.model=model
        self.bullet_count=0
    def add_bullet(self,count):
        self.bullet_count+=count
    def shoot(self):
        '''加完子弹只发1枪'''
        if self.bullet_count<=0:
            print("[%s]没有子弹了..."%self.model)
            return
        self.bullet_count-=1
        print("[%s]突突突,子弹已发。剩余[%s]发"%(self.model,self.bullet_count))
class Soilder:
    def __init__(self,name):
        self.name=name#实例化属性
        self.gun=None#实例化对象
    def fire(self,count):
        '''士兵冲锋陷阵'''
        if self.gun is None:
            print("%s 没有枪"%self.name)#没枪 无计可施
            return
        print("%s 有枪,冲啊!!"%self.name)#有枪开始fire三部曲。fire-1:喊口号
        self.gun.add_bullet(count)#fire-2:加子弹
        for i in range(self.gun.bullet_count):#self.gun.bullet_count
            self.gun.shoot()#fire-3:射击 直到打完

ak47=Gun('AK47')
# ak47.add_bullet(5)
# ak47.shoot()
xusanduo=Soilder("许三多")
xusanduo.gun=ak47###############一个类的属性可以是另一个类的对象object################2
xusanduo.fire(5)
=======================================================================

3、老师学生类
'''
需求:
SchoolPerson类,属性:姓名name、性别sex、年龄age
            方法set_info修改属性、get_info打印信息。定义2个人员进行测试。
            改为__init__构造函数,初始化属性(键盘输入)。定义__del__方法打印“执行了__del__方法”
Student类,继承SchoolPerson,添加额外属性:班级class、学号sno
        添加方法print_info,打印学生所有信息
Teacher类,继承SchoolPerson,添加额外属性:部门department、工号cno
        添加方法print_info,打印教师所有信息
分别调用Student类和Teacher类的print_info,实现信息输出。
'''
class SchoolPerson:
    def set_info(self,name,sex,age):
        self.name=name
        self.sex=sex
        self.age=age
    def get_info(self):
        return "姓名:%s,性别:%s,年龄:%s." %(self.name,self.sex,self.age)
p1=SchoolPerson()
p2=SchoolPerson()
p1.set_info('zs','m',18)
p2.set_info('ls','f',19)
print(p1.get_info(),p2.get_info())


class SchoolPerson2:
    def __init__(self):
        self.name=input("请输入姓名:")
        self.sex = input("请输入性别:")
        self.age = input("请输入年龄:")
    def set_info(self,name,sex,age):
        '''pass'''
        self.name=name
        self.sex=sex
        self.age=age
    def get_info(self):
        return "姓名:%s,性别:%s,年龄:%s." %(self.name,self.sex,self.age)
    def __del__(self):
        '''删除对象'''
        print("执行了__del__方法")

# p3=SchoolPerson2()#'zs','m',18
# p4=SchoolPerson2()#'ls','f',19
# p4.set_info('ls','f',20)#修改了
# print(p3.get_info())
# print(p4.get_info())#最后执行__del__ 挨个对象删除

class Student2(SchoolPerson2):
    def __init__(self,sclass,sno):
        '''父类有init构造函数 没有参数'''
        super().__init__()
        self.sclass=sclass
        self.sno=sno
    def print_info(self):
        print("学生信息::姓名:%s,性别:%s,年龄:%s,班级:%s,学号:%s." %(self.name,self.sex,self.age,self.sclass,self.sno))

student=Student2('一班','07010801')
student.print_info()

class Teacher2(SchoolPerson2):
    def __init__(self,department,cno):
        super().__init__()#父类无参数
        self.department=department
        self.cno=cno
    def print_info(self):
        print("老师信息::姓名:%s,性别:%s,年龄:%s,部门:%s,工号:%s." %(self.name,self.sex,self.age,self.department,self.cno))
teacher=Teacher2('信息化部','709')
teacher.print_info()

变题:如果SchoolPerson2变成传参数
class SchoolPerson2:
    def __init__(self,name,sex,age):
        self.name=name
        self.sex = sex
        self.age = age
那么继承类super写法:
class Teacher2(SchoolPerson2):
    def __init__(self,name,sex,age,department,cno):
        super().__init__(name,sex,age)#父类无参数
        self.department=department
        self.cno=cno
调用写法:
teacher=Teacher2('zs','m',18,'信息化部','709')
teacher.print_info()

变题:如果继承SchoolPerson 父类没有构造函数
   self.name=name#构造自己参数
        self.sex=sex
        self.age=age
总结:父类的属性和参数,子类直接super
     父类没有的属性和参数,子类自己构造self
super好处提现举例:保证多继承时候,公共父类执行1次。执行顺序按照mro.

     

其他:统计调用次数

1、类属性、实例属性 初始化=0,调用方法+1
class Person:
    count=0
    def __init__(self,name):
        self.name=name
        self.scount=0#一个人点名次数
    def get_name(self):
        self.__class__.count += 1
        self.scount+=1
        return "[%s]到"%self.name
    def get_num(self):
        return self.scount
p1=Person('jack')
p2=Person('tom')
print(p1.get_name(),p1.get_name(),p2.get_name())#点名
print(f"实到人数:{Person.count}")#人数统计 类调用次数
print(f"重复点名[{p1.name}]:{p1.scount}次")#对象 调用次数

2、装饰器
def counter(func):
    count = 0
    def wrapper(*args, **kwargs):
        nonlocal count
        count += 1
        print(f"函数{func.__name__}被调用了{count}次")
        return func(*args, **kwargs)
    return wrapper
@counter
def add(a, b):
    return a + b
sum = counter(add)
sum(1, 2)
sum(2, 3)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值