基础复习
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、方法封装:隔离复杂度
子类无法覆盖父类的隐藏属性
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)