本文章主要是自己平时的一些积累,不适合纯纯小白哈,稍微有点基础的可以看看,有错请指正,时间比较久了,可能我也忘记了,欢迎大家一起来讨论还是希望大家点个赞,收个藏,关个注,拜托拜托~~~人生需要鼓励和被鼓励,嘿嘿
文章主要涉及到类与对象中的比较重要的几点:面向对象编程的最显著的几个特征:封装、继承、和多态,魔法方法、函数、重写、钻石继承等......
主要是举了一些例子来展示,这个需要一定基础。本文有些代码是Python交互模式完成的,因为当时还不会使用pycharm,惭愧惭愧~,所以大家实践的时候注意下!
目录
一、 python既是面向过程编程,又是面向对象编程(封装、继承、多重继承)
05.车辆管理系统(基类、派生类,即子类父类[super().init(self,year,model)函数,继承父类的变量])继承***
02.重写(直接通过类名访问类里面的方法的做法称之为调用未绑定的父类方法)
02. surper()函数依靠于MRO(Method Resolution Order)来运行:方法解析顺序 C.mro()或者C.mro
例02 自定义函数如何实现多态接口,接收不同的对象作为参数,并且不检查其类型的情况下执行它的方法
例02 单个下横线开头的变量 单个下横线结尾的变量分别代表什么
slots(副作用,没有动态添加属性的能力) 注:继承自父类的slots属性是不会在子类中生效的
例02 del方法(垃圾回收机制,只有当对象没有任何引用的时候才会被执行)
总结:虽然__del__方法在这个类中被定义了,但你不能直接控制它何时被调用。它的调用是由Python的垃圾回收机制管理的,并且这个机制通常是在需要回收内存时才会运行的。
一、 python既是面向过程编程,又是面向对象编程(封装、继承、多重继承)
01.定义一个点类(封装)
# 定义一个点类
import math
class Point():
#__init__对x,y进行初始化
def __init__(self,x = 0,y = 0):
self.__x = x
self.__y = y
def move(self,dx,dy):
self.__x += dx
self.__y += dy
def distance_to_origin(self):
return math.sqrt(self.__x**2 + self.__y**2)
p = Point(3,4)
print(p.distance_to_origin())
02.创建一个矩形类
# 创建一个矩形类
题目描述:
创建一个名为 Rectangle 的类,用来描述矩形,包括:
私有属性 width 和 height(矩形的宽和高)。
初始化方法,设置 width 和 height 的初始值。
方法 area(),返回矩形的面积。
方法 perimeter(),返回矩形的周长。
# 3.创建一个矩形类:计算面积和周长的方法
class Rectangle:
#进行初始化
def __init__(self,width,height): #__init__:表示构造函数
self.width = width
self.height = height
def area(self):
return self.width * self.height
def perimeter(self):
return (self.width + self.height)*2
# 调用
r = Rectangle(4,5)
print(r.area())
print(r.perimeter())
'''
03.银行账户类(定义私有属性,方法)
# 银行账户类
题目描述:
设计一个名为 BankAccount 的类,用来模拟银行账户,包括:
私有属性 balance(账户余额)。
初始化方法,接收一个初始存款额度。
方法 deposit(amount),存款。
方法 withdraw(amount),取款,如果账户余额不足,打印警告。
'''
'''
class BankAccount:
# 使用双下划线来表示私有属性
def __init__(self,balance = 0):
self.__balance = balance
def deposit(self,amount):
self.__balance += amount
def withdraw(self,amount):
if amount > self.__balance:
print('余额不足')
else:
self.__balance -= amount
def get_balance(self):
return self.__balance
# 调用
BK = BankAccount(1000)
BK.deposit(500)
BK.withdraw(600)
print(BK.get_balance())
BK.withdraw(1000)
'''
04.学生信息管理(init表示构造函数)
'''
'''
练习题4: 学生信息管理
题目描述:
编写一个名为 Student 的类,用以存储一个学生的信息,包括:
私有属性 name 和 scores(学生名字和成绩列表)。
初始化方法,设置学生的名字和成绩。
方法 add_score(score),添加一个新的成绩到列表中。
方法 average(),返回学生的平均成绩。
'''
'''
class Student:
def __init__(self,name,scores = None):
if scores is None:
scores = []
self.__name = name
self.__scores = scores
def add_score(self,score):
self.__scores.append(score)
def average(self):
if self.__scores:
return sum(self.__scores) / len(self.__scores)
return 0
# 调用
Stu = Student('张三')
Stu.add_score(95)
Stu.add_score(88)
Stu.add_score(95)
print(Stu.average())
'''
05.车辆管理系统(基类、派生类,即子类父类[super().init(self,year,model)函数,继承父类的变量])继承***
# 车辆管理系统
题目描述:
实现一个名为 Vehicle 的基类,以及两个派生类 Car 和 Truck,其中:
Vehicle 类包含属性 year(年份), make(制造商)和 model(型号),所有属性为私有。
Vehicle 类还包含方法 description(),返回一个包含年份、制造商和型号的字符串。
Car 类增加属性 doors(车门数),覆盖 description() 方法以包括门数。
Truck 类增加属性 bed_length(车厢长度),覆盖 description() 方法以包含车厢长度。
'''
'''
class Vehicle:
def __init__(self,year,make,model):
self.__year = year
self.__make = make
self.__model = model
def description(self):
str = f'年份为:{self.__year},制造商是:{self.__make},型号是:{self.__model}'
return str
class Car(Vehicle):
def __init__(self,year,make,model,doors):
super().__init__(self,year,model)
self.doors = doors
def description(self):
str = f'年份为:{self.__year},制造商是:{self.__make},型号是:{self.__model},门数是{self.doors}'
return str
class Truck(Vehicle):
def __init__(self, year, make, model, bed_length):
super().__init__(self, year, model)
self.bed_length = bed_length
def description(self):
return f'年份为:{self.__year},制造商是:{self.__make},型号是:{self.__model},车厢长度是{self.bed_length}'
car = Car(2019, "Toyota", "Camry", 4)
truck = Truck(2020, "Ford", "F150", "5.5 ft")
print(car.description()) # 输出: 2019 Toyota Camry with 4 doors
print(truck.description()) # 输出: 2020 Ford F150 with a bed length of 5.5 ft
06.组合(self是实例化对象本身)
# 组合
class Turtle:
def say(self):
print('不积跬步无以至千里!')
class Cat:
def say(self):
print('喵喵喵~')
class Dog:
def say(self):
print('呦呵,我是一只修狗~')
# 现在我们想把小狗和小猫放在一个花园里面,很明显两者之间并不是继承关系,这时候我们就用到了组合
class Garden:
t = Turtle()
c = Cat()
d = Dog()
def say(self):
self.t.say()
self.d.say()
self.c.say()
g = Garden()
g.say()
运行结果: 不积跬步无以至千里! 呦呵,我是一只修狗~ 喵喵喵~
07.绑定(dict进行内省)
# 定义一个空类(可以当做字典来存储数据)
class C :
pass
C.x = 250
C.y = "小甲鱼"
C.z = [1,2,3]
print(C.x)
print(C.y)
print(C.z)
# 生成实例对象的方法
class C:
pass
c = C()
c.x = 250
c.z = [1,2,3]
c.y = "小甲鱼"
c.y = 660
print(c.__dict__)
class C:
def set_x(self,v):
self.x = v
c = C()
print(c.__dict__) #{}
c.set_x(250)
print(c.__dict__) #{'x': 250}
print(c.x) #250
class C:
x = 100
def set_x(self,v):
x = v
c = C()
c.set_x(250)
print(c.x) #100
print(C.x) #100
C.x = 250
print(c.x) #250
c.__dict__ #{}
二、构造函数、重写、钻石继承
01.构造函数
class C: def __init__(self,x ,y): self.x = x #等号左边是绑定到实例化对象里面的x属性,等号右边的x是传进来的参数 self.y = y def add(self): return self.y+self.x def mul(self): return self.y*self.x c = C(2,3) print(c.mul()) print(c.add()) # 内省 print(c.__dict__) #{'x': 2, 'y': 3}
02.重写(直接通过类名访问类里面的方法的做法称之为调用未绑定的父类方法)
# 重写:直接通过类名访问类里面的方法的做法称之为调用未绑定的父类方法,可能会出现‘钻石问题’ class D(C): def __init__(self,x,y,z): C.__init__(self,x,y) #super关键字 super().__init__(self,year,model) self.z = z def add(self): return C.add(self) + self.z def mul(self): return C.mul(self) * self.z d = D(2,3,4) print(d.mul()) print(d.add())
03.钻石继承(调用super函数来解决)
# 钻石继承 class A: def __init__(self): print('Hello 我是A~') class B1(A): def __init__(self): A.__init__(self) print('Hello 我是B1') class B2(A): def __init__(self): A.__init__(self) print('Hello 我是B2~') # 多继承 class C(B1,B2): def __init__(self): B1.__init__(self) B2.__init__(self) print('Hello 我是C~') c = C()
Hello 我是A~ Hello 我是B1 Hello 我是A~ Hello 我是B2~ Hello 我是C~
01. 解决方法:super()函数(避免重复调用)
class A: def __init__(self): print('Hello 我是A~') class B1(A): def __init__(self): super().__init__() print('Hello 我是B1') class B2(A): def __init__(self): super().__init__() print('Hello 我是B2~') # 多继承 class C(B1,B2): def __init__(self): super().__init__() print('Hello 我是C~') c = C()
Hello 我是A~ Hello 我是B2~ Hello 我是B1 4Hello 我是C~
02. surper()函数依靠于MRO(Method Resolution Order)来运行:方法解析顺序 C.mro()或者C.mro
print(C.mro()) #[<class '__main__.C'>, <class '__main__.B1'>, <class '__main__.B2'>, <class '__main__.A'>, <class 'object'>]
三、类与对象案例分析(继承类的顺序为从左至右的顺序来继承)
01.Mixin案例
# 类与对象案例分析:Mixin(混入,乱入)4.22
class Animal:
def __init__(self,name,age):
self.name = name
self.age = age
def say(self):
print(f'我叫{self.name},今年{self.age}岁')
class FlyMixin:
def fly(self):
print('哦吼,我还会飞')
class Pig(FlyMixin,Animal): #此处pig类继承于FlyMixin、Animal父类
def special(self):
print('我的技能是拱大白菜!')
# 调用 p = Pig('哈哈',5) p.say() p.special() p.fly()
02.案例分析
首先按住奥从左至右的顺序继承类LoggerMinin中的display方法,super()方法因为严格遵守MRO原则所以先跳到Dispalyer类中执行从而打印“This is a text”,然后执行log函数取决于self,来自于绑定***(M...里面的函数)
四、多态和鸭子类型(鸭子类型(它的行为是否合乎规矩))
例01 多态(不同场景不同表现形式)
class Shape: def __init__(self,name): self.name = name def area(self): pass class Circle(Shape): def __init__(self,radius): super().__init__("圆形") self.radius = radius def area(self): return 3.14 * self.radius**2 class Square(Shape): #正方形 def __init__(self,length): super().__init__('正方形') self.length = length def area(self): return self.length*self.length class Triangle(Shape): #三角形 def __init__(self,x,y): super().__init__('三角形') self.x = x self.y = y def area(self): return self.x * self.y * 1/2 s = Square(5) t = Triangle(4,5) c = Circle(2) print(s.name) print(t.name) print(c.name) print(s.area()) print(t.area()) print(c.area())
例02 自定义函数如何实现多态接口,接收不同的对象作为参数,并且不检查其类型的情况下执行它的方法
# 自定义函数如何实现多态接口,接收不同的对象作为参数,并且不检查其类型的情况下执行它的方法
class Cat:
def __init__(self,name,age):
self.name = name
self.age = age
def intro(self):
print(f'我是一只猫,我叫{self.name},今年{self.age}岁啦~~~')
def say(self):
print('miao~~~')
class Dog:
def __init__(self,name,age):
self.name = name
self.age = age
def intro(self):
print(f'我是一只修狗,我叫{self.name},今年{self.age}岁啦~~~')
def say(self):
print('呦吼~~~')
class Pig:
def __init__(self,name,age):
self.name = name
self.age = age
def intro(self):
print(f'我是一只小猪,我叫{self.name},今年{self.age}岁啦~~~')
def say(self):
print('oink~~~')
# 实例化对象
c = Cat('hah',5)
p = Pig('huh',3)
d = Dog('布布',6)
# 多态
def animal(x):
x.intro()
x.say()
animal(c)
animal(d)
animal(p)
我是一只猫,我叫hah,今年5岁啦~~~ miao~~~ 我是一只修狗,我叫布布,今年6岁啦~~~ 呦吼~~~ 我是一只小猪,我叫huh,今年3岁啦~~~ oink~~~
五、私有变量和slots
例01 私有变量(名字改编是发生在类实例化对象的时候)
# '私有变量'和__slots__ # name mangling class C: def __init__(self,x): self.__x = x #__开头表示私有变量 def set_x(self,x): self.__x = x def get_x(self): print(self.__x) # 想要访问私有变量的值就要通过接口 c = C(250) # c.__x 这样是没有办法访问的 c.get_x() c.set_x(520) c.get_x() print(c.__dict__) #__dict__ 是一个特殊的内置属性,它返回对象的属性字典 print(c._C__x)
250 520 {'_C__x': 520} 520
class D: def __func(self): print('Hello FishC') d = D() #AttributeError: 'D' object has no attribute '__func'. Did you mean: '_D__func'? # d.__func() d._D__func() #Hello FishC
例02 单个下横线开头的变量 单个下横线结尾的变量分别代表什么
单个下横线开头的变量:仅供内部使用的变量 (不要随意访问)
单个下横线结尾的变量:例如class
例03 效率提升
python由于灵活性有时候会抛弃很多内存空间
# 利用字典来存储属性,为什么字典效率快,舍时间得空间 class C: def __init__(self,x): self.x = x c = C(520) c.y = 250 #动态添加属性 print(c.__dict__) c.__dict__['z'] = 666 print(c.__dict__)打印 {'x': 520, 'y': 250} {'x': 520, 'y': 250, 'z': 666}
slots(副作用,没有动态添加属性的能力) 注:继承自父类的slots属性是不会在子类中生效的
class C:
__slots__ = ["x","y"]
def __init__(self, x):
self.x = x
c = C(250)
c.y = 520
print(c.y)
# c.z = 666 #AttributeError: 'C' object has no attribute 'z'
class D:
__slots__ = ['x','y']
def __init__(self,x,y,z):
self.x = x
self.y = y
self.z = z
# AttributeError: 'D' object has no attribute 'z'
# d = D(3,4,5)
六、魔法方法
例01 改变不可变对象
init(self[,..])方法不需要调用也会执行
class CapStr(str): def __new__(cls,string): string = string.upper() return super().__new__(cls,string) cs = CapStr("FishC") print(cs) # string是不可变对象,但是在这里是可以修改的,是因为我们赶在实例对象被创建之前就进行了拦截 # 然后再去调用super()函数去创建真正的实例 print(cs.lower()) print(cs.capitalize()) #首字母大写 # FISHC # fishc # Fishc
例02 del方法(垃圾回收机制,只有当对象没有任何引用的时候才会被执行)
总结:虽然__del__
方法在这个类中被定义了,但你不能直接控制它何时被调用。它的调用是由Python的垃圾回收机制管理的,并且这个机制通常是在需要回收内存时才会运行的。
class C: def __init__(self): print("我来了") def __del__(self): print("我走了") c = C() del c c = C() d = c del c del d 运行结果: 我来了 我走了 我来了 我走了
例03 对象重生
class D: def __init__(self,name): self.name = name def __del__(self): global x x = self d = D('小甲鱼') print(d) #<__main__.D object at 0x0000015B2D361DF0> del d print(x) #<__main__.D object at 0x0000022ABC121E50> print(x.name) #小甲鱼
class E: def __init__(self,name,func): self.name = name self.func =func def __del__(self): self.func(self) def outter(): x = 0 def inner(y = None): nonlocal x if y: x = y else: return x return inner f = outter() e = E('小甲鱼',f) print(e) del e g = f() print(g) print(g.name) #运行结果 <__main__.E object at 0x000001EF15342090> <__main__.E object at 0x000001EF15342090> 小甲鱼
七、运算相关的魔法方法
Python--魔法方法汇总 - 知乎 (zhihu.com)
例01 重写add方法 注意(self,other)
# 重写add方法
# 注意(self,other)
class S(str):
def __add__(self, other):
return len(self) + len(other)
s1 = S('python')
s2 = S('hhhhh')
print(s1+s2) #11
print(s1 +'python') #12
print('FichC'+s2) #FichChhhhh
例02 radd ()方法调用前提
注:当两个对象相加的时候,如果左侧的对象和右侧的对象不同类型,并且左侧的对象没有定义add()方法,或者其add()返回Notlmplemented,那么Python就会去右侧的对象中找查找是否有radd()方法的定义。
能实现的前提是左侧的方法中一定返回值是NotImplemented或者没有返回值
class S1(str):
def __add__(self, other):
return NotImplemented #表示该方法未实现
class S2(str):
def __radd__(self, other):
return len(self)+len(other)
s1 = S1("Apple")
s2 = S2("Banana")
print(s1+s2)
注:此处为什么输出的s2是两个字符串的拼接,因为要实现radd()是有条件的,要基于不同的类的对象,因为s2+=s2其继承的类是同一个类,此时就会调用父类str的add()方法得到的是两个字符串的拼接。
class S2(str):
def __radd__(self, other):
return len(self)+len(other)
# s1 = S1("Apple")
s2 = S2("Banana")
# print(s1+s2)
class S1(str):
def __iadd__(self, other):
return len(self) + len(other)
s1 = S1("Apple")
s1 += s2
print(s1) #11
print(type(s1)) #<class 'int'>
s2 += s2
print(s2) #BananaBanana
print(type(s2)) #<class 'str'>
例03 将一个对象转换成整数类型
class ZH_INT:
def __init__(self,num):
self.num = num
def __int__(self):
try:
return int(self.num)
except ValueError:
zh = {"零":0,"一":1,'六':6,"七":7,"八":8,"三":3,'四':4,"壹":1}
result = 0
for each in self.num:
if each in zh:
result += zh[each]
else:
result += int(each)
result *= 10
return result
n = ZH_INT("520")
print(int(n))
n = ZH_INT("一一零")
print(int(n))
运行结果: 520 1100
例04 & | ~ ^(异或) << >>
# 按位的与或非是对两个整数进行位运算 print(3&2) #2 print(3&4) #0 ''' 按位与运算只有两个数字的二进制全部都是1是时候得出来的结果才会是1其余位置为零 所以3:011 4:100 所以与的结果是0 ''' # 查看数字的二进制 print(bin(2)) #0b10 print(bin(3)) #0b11 # 按位或:| ''' 当两个数字的二进制有1存在的时候得出来的结果都是1,然后转换成十进制数字 ''' print(3|4) print(3|2) # 按位非:~ # 涉及到取反的内容略 print(~2) print(~3)# 按位异或:当两个相同的二进制位值不一样的时候那么结果对应的的二进制的值为1 print(3^2) #1 print(3^4) #7 # << >>:左移和右移运算符 print(bin(8)) #0b1000 # 8右移两位 print(8>>2) #2 # 左移补零,右移n位就是除以2的n次方 #左移n位乘以2的n次方 print(8//pow(2,2)) #2,//在python中代表整数除法的意思,/代表出来的是浮点数 import math 0.1+0.2 == 0.3 + math.ulp(0.3)
八、 index魔法方法 ,与属性相关的函数和魔法方法
例01 当对象作为索引值被调用的时候才会被调用
# __index__魔法方法 # 当对象作为索引值被调用的时候才会被调用
class C:
def __index__(self):
print("被拦截了")
return 3
c = C()
# c[2] #TypeError: 'C' object is not subscriptable
s = "FishC"
print(s[c]) #被拦截了 h
print(bin(c)) #0b11
例02 属性访问(与属性相关的函数和魔法方法)
# 与属性相关的函数和魔法方法 # 属性访问 ''' hasattr() setattr() getattr() delattr() '''
class C:
def __init__(self,name,age):
self.name = name
self.__age = age
c = C('小甲鱼',18)
print(hasattr(c,"name")) #True
print(getattr(c,"_C__age")) #18 私有变量
(setattr(c,"_C__age",19))
print(getattr(c,"_C__age")) #19
delattr(c,"_C__age")
例03 getattr
class C:
def __init__(self,name,age):
self.name = name
self.__age = age
def __getattribute__(self, attrname):
print("拿来吧你~")
return super().__getattribute__(attrname)
c = C("小甲鱼",18)
print(getattr(c,"name"))
print(c._C__age)
''' __getattr__ 也是一个实例方法,但它只在Python无法找到指定的属性时才会被调用。 也就是说,当你尝试访问一个不存在的属性时,Python会首先尝试使用 __getattribute__, 如果失败(即没有找到该属性或 __getattribute__ 抛出了 AttributeError),那么就会调用 __getattr__。 因此,__getattr__ 通常用于实现默认属性、动态属性或懒加载等特性。 '''
例04 setattr
class D:
def __setattr__(self, name, value):
self.__dict__[name] = value
d = D()
d.name = "小甲鱼"
print(d.name)
__setattr__例2
class D:
def __setattr__(self, name, value):
self.__dict__[name] = value
def __delattr__(self, name):
del self.__dict__[name]
d = D()
d.name = "小甲鱼"
print(d.__dict__)
# print(d.name) #小甲鱼
del d.name
print(d.__dict__)
# {'name': '小甲鱼'}
# {}
九、索引、切片、迭代协议
例01 切片
# 当对象被索引的时候,Python会怎么做? # __getitem__(self,index) class C: def __getitem__(self, index): print(index) c = C() c[2] #2 c[2:8] #slice(2, 8, None表示步长) s = "I love you" print(s[2:6]) print(s[slice(2,6)]) print(s[7:]) print(s[slice(7,None)]) print(s[::4]) print(s[None,None,None])
注:如果序列是“123456” [:-2]则后面的数字取不到,即得到的结果是1234
若[-2:]则得到的结果是56