目录
1.类和对象的含义
你可以根据一张苹果派食谱(类)制作出任意多个苹果派(对象)
类就像模板一样,对象是具体的实例
对象是类的一个实例,你可以创建同种类的多个对象
对象就是实例,实例就是对象
- 数据域 => 属性
- 行为 => 方法
2.定义类和创建对象
定义类
class ClassName:
initializer
methods
使用类的特殊方法:__init__(左右各是两根下划线,中间是 init)来初始化
初始化程序,用来完成一系列初始化动作,用来设置对象的初始属性
import math
class Circle:
def __init__(self, radius=1):
self.radius = radius
def get_perimeter(self):
return 2 * self.radius * math.pi
def get_area(self):
return math.pi * (self.radius ** 2)
def set_radius(self, radius):
self.radius = radius
在上面的这个例子中,定义了一个Circle类:
- 如果需要创建基于该类的对象,需要传递参数radius,默认值为1。
- 在创建对象时,会进行初始化操作,生成一个成员self.radius,其初始值为传递的参数值radius。
- 另外还对该类定义了三个方法get_perimeter,get_area,set_radius
- self代表类本身。比如self.radius表示该类所具有的属性成员,而radius是形参等外部成员或者局部变量。
创建对象和调用成员
创建对象语法:object_var = ClassName(参数)
调用成员:使用圆点运算符(.)
# 创建一个圆对象
c1 = Circle(2) # 类名(参数 1,参数 2,参数 n)
# 访问圆对象的数据域
r1 = c1.radius
print(r1) # 2
# 调用对象的方法
area1 = c1.get_area()
print(area1) # 12.566370614359172
默认初始化
import math
class Circle:
radius = 1
width = 5
height = 10
def get_perimeter(self):
return 2 * self.radius * math.pi
def get_area(self):
return math.pi * (self.radius ** 2)
def set_radius(self, radius):
self.radius = radius
c1 = Circle()
print(c1.radius) # 1
print(c1.width) # 5
print(c1.height) # 10
在这个例子中,尽管radius = 1 ,width = 5 和height = 10前面没有加self,但是由于没有定义__init__特殊方法,系统默认进行了初始化,生成了三个成员变量,self.radius,self.width,self.height。
self 参数
self = 对象本身
在类的内部使用self.x 访问实例的属性 x,使用语法 self.method()来调用实例的方法 method()
import math
class Circle:
radius = 1
width = 5
height = 10
def get_area(self):
return math.pi * (self.radius ** 2)
def set_radius(self, radius):
self.radius = radius
def func(self):
self.set_radius(2)
ret = self.get_area()
print(ret) # # 12.566370614359172
c1 = Circle()
c1.func()
类的构造函数和析构函数
__init__特殊方法为构造函数,在创建对象时触发
__del__特殊方法为析构函数,在对象销毁时触发。通常如果不使用del关键字主动销毁对象,在程序结束时对象才会被销毁。
class Worker:
def __init__(self): # __init__ 初始化的时候调用
print("构造了", id(self))
def __del__(self): # __del__ 删除的时候调用
print("删除了", id(self))
w1 = Worker()
w2 = Worker()
'''
构造了 1655680304072
构造了 1655680304968
删除了 1655680304072
删除了 1655680304968
'''
3.有名对象和匿名对象
# 有名对象
c1 = Circle(2)
# 匿名对象
Circle(2)
匿名对象的生命周期很短,在创建时进行初始化操作后立马就消失了。
而有名对象只要不主动进行删除,就一直存在在内存中,直到程序结束。
4.不同对象的属性和方法的地址
- 同一个类的不同对象的地址不同。
- 同一个类的不同对象的方法的地址相同。方法的地址相同说明方法是共享的,用 self 来区别是谁调用的 。
- 同一个类的不同对象的属性的地址,在属性相等时是相同的,在属性不等时是不同的
import math
class Circle:
def __init__(self, radius=1):
self.radius = radius
def get_area(self):
return math.pi * (self.radius ** 2)
c1 = Circle(1)
c2 = Circle(1)
c3 = Circle(2)
print(id(c1) == id(c2)) # False
print(id(c2) == id(c3)) # False
print(id(c3) == id(c1)) # False
print(id(c1.radius) == id(c2.radius)) # True
print(id(c2.radius) == id(c3.radius)) # False
print(id(c3.radius) == id(c1.radius)) # False
print(id(c1.get_area) == id(c2.get_area)) # True
print(id(c2.get_area) == id(c3.get_area)) # True
print(id(c3.get_area) == id(c1.get_area)) # True
5.对象的引用与拷贝
对象赋值是引用
class Complex:
def __init__(self, x, y):
self.x = x
self.y = y
def show(self):
print(self.x, "+", self.y, "i")
c1 = Complex(1, 2)
c2 = c1 # 引用,好处是节约内存
c3 = Complex(c1.x, c1.y)
# 新创建一个对象,成员属性和原来一样,但两对象地址不同,这是拷贝,保证数据的安全
print(id(c1) == id(c2)) # True
print(id(c1) == id(c3)) # False
print(id(c2) == id(c3)) # False
c1.x = 100
c1.show() # 100 + 2 i
c2.show() # 100 + 2 i
c3.show() # 1 + 2 i
由于对象赋值是引用,因此如果将对象作为函数实参传递给形参,如果函数内部对形参进行了值修改,则实参的值也会被修改。例子如下:
import math
class Circle:
def __init__(self, radius=1):
self.radius = radius
def get_area(self):
return math.pi * (self.radius ** 2)
def print_radius_area(c, times):
print("radius\t\tarea")
while times >= 1:
print(c.radius, "\t\t", round(c.get_area(), 2))
c.radius += 1
times -= 1
def main():
c1 = Circle()
n = 5
print("\nradius is", c1.radius)
print("n is ", n, "\n")
print_radius_area(c1, n)
print("\nradius is", c1.radius)
print("n is ", n, "\n")
main()
"""
radius is 1
n is 5
radius area
1 3.14
2 12.57
3 28.27
4 50.27
5 78.54
radius is 6
n is 5
"""
用拷贝保证数据安全
import math
class Circle:
def __init__(self, radius=1):
self.radius = radius
def get_area(self):
return math.pi * (self.radius ** 2)
def print_radius_area(c, times):
# print_radius_area(c1, n)函数调用时,c1赋值给c,此时c是对c1的引用
c = Circle(c.radius)
# c = Circle(c.radius),Circle(c.radius)创建了一个新对象,然后赋值在c
# 此时c不再是对c1的引用,因此后序对c的成员的改变不会影响到c1
print("radius\t\tarea")
while times >= 1:
print(c.radius, "\t\t", round(c.get_area(), 2))
c.radius += 1
times -= 1
def main():
c1 = Circle()
n = 5
print("\nradius is", c1.radius)
print("n is ", n, "\n")
print_radius_area(c1, n)
print("\nradius is", c1.radius)
print("n is ", n, "\n")
main()
6.数据私有化
- 通过数据域私有来保护数据,让类更容易维护,因为不私有时,数据可能被自己无意修改掉而不知,或者被别人篡改
- 在不私有的情况下,可以在外部直接通过 r = c.radius 访问圆的数据域,可以在外部通过 c.radius = 2 来修改圆的数据域
- 在私有的情况下,只能在类内部修改,或者通过特定的方法从外部访问
-
在特定的方法那里加入一些权限,比如要输入密码等,就可以很好地保护数据(比如保护银行余额等)
self.__radius = radius
def get_radius(self):
return self.__radius
def set_radius(self, radius):
self.__radius = radius
def __get_area(self):
return math.pi * (self.radius ** 2)
class BankCard:
def __init__(self):
self.__money = 100
def 存款(self, num):
self.__money += num
def 取款(self, num, password):
if password == "123456":
self.__money -= num
else:
print("密码错误请重新输入")
def 查询(self, password):
if password == "123456":
print(self.__money)
else:
print("密码错误请重新输入")
李鑫的钱钱 = BankCard()
李鑫的钱钱.查询("123456") # 100
李鑫的钱钱.存款(100000)
李鑫的钱钱.查询("123456") # 100100
李鑫的钱钱.取款(50000, "123456") # 私有设计增加权限
李鑫的钱钱.查询("123456") # 50100
李鑫的钱钱.取款(50000, "456") # 密码错误请重新输入
# 外部访问私有成员将会出错
# print(李鑫的钱钱.__money)
# AttributeError: 'BankCard' object has no attribute '__money'
# 外部修改私有成员将不会成功
李鑫的钱钱.__money = -100000000 # 安全权限设计
print(李鑫的钱钱.__money) # -100000000
李鑫的钱钱.查询("123456") # 50100
7.动态增加方法
在对象创建好后再给对象增加方法
import math
class Circle:
def __init__(self, radius=1):
self.radius = radius
self.area = 0
def get_perimeter(self):
return 2 * self.radius * math.pi
c1 = Circle()
c2 = Circle(2)
# 此时c1只有两个方法get_perimeter和set_radius
# 现在我们对其增加一个方法
c1.print_info = lambda user: print(user, " get c1 info: radius =", c1.radius)
c1.print_info("tom") # tom get c1 info: radius = 1
# 再增加一个方法
def __get_area(m_c: Circle):
m_c.area = math.pi * m_c.radius**2
c1.get_area = lambda: __get_area(c1) # 匿名函数不传递参数
c1.get_area()
print(c1.area) # 3.141592653589793
8.运算符重载
# 为对象定义加法
class Complex:
def __init__(self, x, y):
self.x = x
self.y = y
def show(self):
print(self.x, "+", self.y, "j")
def __add__(self, other):
if type(other) == type(self):
_x = self.x + other.x
_y = self.y + other.y
return Complex(_x, _y)
elif type(other) == int:
return Complex(self.x + other, self.y)
c1 = Complex(1, 2)
c2 = Complex(3, 5)
c1.show() # 1 + 2 j
c2.show() # 3 + 5 j
c3 = c1 + c2
c3.show() # 4 + 7 j
c4 = c1 + 5
c4.show() # 6 + 2 j
运算符重载函数表
注意:你可以传递一个对象去调用print(x),这等价于调用print(x.__str__())或print( str(x) ),因此想调用print对自定义对象进行打印,需要重载__str__方法。
class Complex:
def __init__(self, x, y):
self.x = x
self.y = y
def __add__(self, other):
if type(other) == type(self):
_x = self.x + other.x
_y = self.y + other.y
return Complex(_x, _y)
elif type(other) == int:
return Complex(self.x + other, self.y)
def __str__(self):
ret = f"complex: ({self.x}+{self.y}i)"
return ret
c1 = Complex(1, 2)
c2 = Complex(3, 5)
print(c1) # complex: (1+2i)
print(c2) # complex: (3+5i)
c3 = c1 + c2
print(c3) # complex: (4+7i)
c4 = c1 + 5
print(c4) # complex: (6+2i)
9.继承
单继承
例子1:
class 王健林:
def __init__(self):
self.money = 100000000
self.mystr = "定个小目标,赚它一个亿"
def doing(self):
print("疯狂工作")
def buy(self):
print("有钱花")
class 王思聪(王健林):
def _init__(self):
super().__init__() # 对所有父类对象初始化
# 等价于: 王健林.__init__(self) # 但前者所全部父类对象初始化
self.mystr = "我交朋友不看钱,因为谁都没有我有钱"
def doing(self):
print("我是国民老公,想泡谁就泡谁")
dawang = 王健林()
dawang.doing() # 疯狂工作
print("王健林 money", dawang.money) # 王健林 money 100000000
print(dawang.mystr) # 定个小目标,赚它一个亿
print("-------------")
xiaowang = 王思聪()
xiaowang.doing() # 我是国民老公,想泡谁就泡谁
print("王思聪 money", xiaowang.money) # 王思聪 money 100000000
xiaowang.buy() # 有钱花
print(xiaowang.mystr) # 定个小目标,赚它一个亿
例子2:父类初始化:带上参数
class Parent:
def __init__(self, work_time, money):
self.work_time = work_time
self.money = money
class Child(Parent):
def __init__(self, work_time, money, play_time):
super().__init__(work_time, money)
self.play_time = play_time
oneChild = Child(20, 200, 30)
print(oneChild.money) # 200
多重继承
# 多个父类
class Parent1:
def __init__(self):
self.money = 100
def doing(self):
print(f"money={self.money}, 正在赚钱")
class Parent2:
def __init__(self):
self.money = 1000000
def doing(self):
print(f"money={self.money}, 正在玩耍")
# 多个父类时,若父类们有相同的属性名或者方法名,继承最先出现的父类
class Child(Parent2, Parent1):
def __init__(self):
super().__init__()
def buy(self):
self.money = 0
boy = Child()
print(boy.money) # 1000000
boy.doing() # money=1000000, 正在玩耍
boy.buy()
print(boy.money) # 0
私有属性和方法不可继承
class Parent:
def __init__(self):
self.money = 100
self.__wife = "刘亦菲"
def __doing(self):
print(f"money={self.money}, 正在赚钱")
class Child(Parent):
def __init__(self):
super().__init__()
def buy(self):
self.money = 0
boy = Child()
# print(boy.__wife) # AttributeError: 'Child' object has no attribute '__wife'
# boy.__doing() # AttributeError: 'Child' object has no attribute '__doing'
object 类
class son1:
pass
class son2(object):
pass
object1 = son1()
print(dir(object1)) # dir 函数查看对象的所有属性和方法
object2 = son2()
print(dir(object2))
print(dir(object))
多态(动态绑定)
类型兼容原则:需要传递父类的参数,那么其子类也可以被传递。
class Sever:
def make(self):
print("生产食物")
class MakeApple(Sever):
def make(self):
Sever.__init__(self)
print("生产苹果汽水")
def make_product(server_product: Sever):
server_product.make()
print(isinstance(MakeApple(), Sever)) # True
m = Sever()
m1 = MakeApple()
make_product(m) # 生产食物
make_product(m1) # 生产苹果汽水
多态(动态绑定)含义:一个方法可以沿着继承链被很多子类继承,运行时具体调用哪个子类的该方法,由 python 动态决定。动态绑定的机制如下:
![](https://img-blog.csdnimg.cn/direct/341c227d754c4dddb89acfd7935b7b2c.png)
class People:
def doing(self):
print("freedom")
class GrandParent(People):
def doing(self):
print("live")
class Parent(GrandParent):
def doing(self):
print("work")
class Child(Parent):
pass
obj = Child()
obj.doing() # work
10.isinstance 函数
x = 10
mystr = "好好学习"
print(isinstance(x, int)) # True
print(isinstance(x, float)) # False
print(isinstance(mystr, str)) # True
class Father:
pass
class Son(Father):
pass
print("-----")
print(isinstance(Father(), Father)) # True
print(isinstance(Son(), Father)) # True # 子类属于父类
print(type(Father())) # <class '__main__.Father'>
print(type(Father)) # <class 'type'>
print(type(Father()) == type(Father)) # False
print(type(Son()) == type(Father)) # False # type 对类型进行严格检查
11.静态方法
- 普通方法:对于类的普通方法,不同对象的该方法地址不同,不是同一个方法
- 静态方法:对于类的静态方法,不同对象的共享该方法,同一个地址
class People:
def __init__(self, tall):
self.tall = tall
@staticmethod # 静态方法
def get_eyes():
return 2
# @classmethod # 标识符,可以省略,类的内部方法
def get_tall(self):
return self.tall
print(People.get_eyes()) # 通用的东西,静态方法
# print(People.get_tall()) # 这个就不行了,因为get_tall不是静态方法
p1 = People(175)
print(p1.get_eyes()) # 实例化的对象也可以调用静态方法
print(p1.get_tall()) # 实例化的对象也可以调用
end