class 类名[(object)]: 默认继承object类 多个对象相同的特征可以抽取出来,就是这个类的属性 多个对象相同的动作行为抽取出来,就是这个类的方法 例如: 手机的属性: 颜色,型号,大小,价格 手机的方法:打电话,发短信,打游戏 就可以将手机抽取出来一个类
# 定义类
class Phone:
# 属性
pass
# 方法
# python有个特点,可以使用空类(class)去构建 # 当python解释器遇到类时,就会将这个类(Phone)当成'模型'存入内存,后面使用这个模型去创建对象 'xx = Phone()' # 类名后加()表示创建对象 'xx = Phone()' xx会在内存一块地址中保存 # 调用对象内的属性和方法 xx.prop xx.func
# 使用类创建对象
# Phone() 动作: 1-找到Phone在内存中的空间地址
# 2-根据Phone类向内存申请一块内存空间
# 3-如果没有__init__(self)方法则对象创建结束
# 4-如果有则执行此方法,将申请的内存空间地址赋值给__init__(self)的self,再把地址赋值给对象
xxx = Phone()
print(Phone) #
print(xxx) # <__main__.Phone object at 0x0000023C7D2E74C0>
# 当对象调用类属性时,首先会在自己的内存空间中去找,如果没有这个属性才会去类空间中去找.
class Person:
name = 'bot01'
age = 18
gender = 'male'
bot = Person()
print(bot.name) # bot01
print(bot.age) # 18
print(bot.gender) # male
# 在对象的操作是不会更改掉类本身的属性的,如果想要更改类当中的属性初始值则需要 类名.变量名 = new value
Person.name = 'bot02'
print(bot.name) # bot02 默认初始值已经被修改了
# 类与方法 ->写在类里面的函数
# 方法种类: 普通方法 类方法 静态方法 魔术方法
# 普通方法:
class Car:
brand = 'five'
price = 10000
type = 'five_001'
# 普通方法 self:表示当前调用这个方法的对象的内存地址
def run(self):
print(f'self = {self}') # self = <__main__.Car object at 0x00000202C17D3E80>
print('正在出发,请系好安全带!')
# 对象传来的属性并不能保证每个调用此方法的对象都有这个属性
print(self.repair) # VIP车辆专修!
# 当对象在创建时会在内存空间开辟出一块内存空间,当对象调用类中的方法时-car1.run()-因为对象本身的内存空间中没有这个方法, # 所以会带着自己的内存空间地址去访问-类模型-的内存地址,并且调用执行类中的-run()-方法 def run(self)中self就是来承接对象的内存地址的 # self的好处是:当对象自身新增某些属性时,可以使用self在类中方法调用这个属性 (感觉有点类似java中的this关键字)
car1 = Car()
car1.repair = 'VIP车辆专修!'
print(car1) # <__main__.Car object at 0x00000202C17D3E80>
car1.run() # self = <__main__.Car object at 0x00000202C17D3E80> 正在出发,请系好安全带!
# 但对象传来的属性,并不能保证每个调用此方法的对象都有这个属性,所以当遇到没有这个属性的对象调用方法时,就会报错并结束运行
car2 = Car()
car2.run() # AttributeError: 'Car' object has no attribute 'repair' 程序运行报错,因为car2和类中都有没repair属性
# 既然方法无法保证每个对象都能有repair这个自定义属性,那么我们应该怎么解决这个问题呢? # 使用魔术方法: def__init__(self): init-初始化 # init方法是在创建对象之前执行的,self会先拿到分配给对象的内存地址,这里的self就代表每个使用类的对象,使用self调用对象达到给属性赋值的功能 # init方法可以让每个对象达到统一性,对象也可以再自定义属性.说白了就是个属性的初始化
class Car:
brand = 'five'
price = 10000
type = 'five_001'
# 添加初始化方法解决这个问题
def __init__(self):
self.repair = '统一售后检修'
# 普通方法 self:表示当前调用这个方法的对象的内存地址
def run(self):
print(f'self = {self}') # self = <__main__.Car object at 0x00000202C17D3E80>
print('正在出发,请系好安全带!')
# 对象传来的属性并不能保证每个调用此方法的对象都有这个属性
print(self.repair) # 日常维修车辆!
car3 = Car()
car3.repair = 'VIP车辆精修'
car3.run() # VIP车辆精修
car4 = Car()
car4.run() # 统一售后检修
# 普通方法: 在类中定义的方法,只有通过对象才能调用的方法 # 我们知道,在类中封装的是公共的特征与公共的行为动作,那当我们需要使用对象不同个体的动作方法或特征时,应该怎么办呢? # 上面我们使用了__init__()初始化方法保证了我们每个对象的统一性,这里也可以使用这个方法解决这个问题 (有点像java类的构造函数) # 再来一遍流程:在p1 = Person(xx,xx)时,Person()的动作: # 1-先在内存中找到Person类的地址 # 2-为p1按Person的模子划分一块内存地址 # 3-将p1划分的内存地址先传递给init的self中,完成初始化函数的运行 # 4-将划分的内存地址再传递给p1变量
class Person:
# 初始化函数 将创建对象传递的参数赋值 这里的self相当于不同的调用的对象的内存空间 **p1\p2...
def __init__(self, name, age):
self.name = name
self.age = age
def self_introduction(self):
print(f'我的名字是{self.name},我今年{self.age}岁了!')
# 方法中我们也可以定义传参运行
def eat(self, food):
print(f'今天想吃{food}')
# 通过上面我们初始化函数的定义,我们就可以根据对象个体不同的方法特征进行不同的传值
p1 = Person('bob', 25)
p1.self_introduction() # 我的名字是bob,我今年25岁了!
p1.eat('红烧茄子') # 今天想吃红烧茄子
p2 = Person('coc', 26)
p2.self_introduction() # 我的名字是coc,我今年26岁了!
p2.eat('鱼香茄子') # 今天想吃鱼香茄子
类方法
# 类方法特点: 1-在方法上加上装饰器@classmethod 2-类方法的参数时cls类而不是self对象 # 3-类方法中不能使用self的对象属性和方法,只能使用类属性和类方法 # 类方法作用: 1-可以完成一些在对象创建之前需要完成的动作
class Dog:
def __init__(self, name):
self.name = name
@classmethod
def class_test(cls):
print(cls) #
print('类方法运行')
def eat(self):
print(f'{self.name}开吃')
d1 = Dog('aoa')
d1.eat() # aoa开吃
d1.class_test() #
# 可以直接使用类调用类方法,无需对象参与
Dog.class_test() # 类方法运行
# 假如上面的Dog类我们需要加入私有属性__age 代表默认狗狗都是初始年龄 但要在后续更改这个年龄怎么办呢? # __变量:表示私有变量 只能在类中被访问
# 定义类方法访问类中的私有属性
class Dog:
__age = 1
@classmethod
def update_age(cls):
cls.__age = 2
print(f'修改后的age:{cls.__age}')
@classmethod
def show_age(cls):
print(f'dog的年龄为{cls.__age}')
d2 = Dog()
d2.show_age() # dog的年龄为1
d2.update_age() # 修改后的age:2
d2.show_age() # dog的年龄为2
静态方法
# 静态方法: # 1-需要装饰器@staticmethod # 2-静态方法def staticmethod()中没有任何参数 # 3-使用类名调用静态方法 # 4-静态方法只能调用类属性和类方法 # 5-随着类的加载,静态方法和类方法会一同加载进内存,所以可以直接使用类名调用
class Student:
@staticmethod
def study():
print('提高一分,干掉千人!')
s1 = Student()
s1.study() # 提高一分,干掉千人!
Student.study() # 提高一分,干掉千人!
# 普通方法总结: 1-没有装饰器 2-需要依赖对象使用 # 静态方法和类方法总结: 1-装饰器不同 2-参数类方法有静态方法没有 3-只能访问类属性和类方法 4-都可以通过类名调用 5-都可以在创建对象之前使用
魔术方法
# 魔法方法: 是类/对象中的方法,和普通方法不同,魔术方法不需要调用,而是在特定的时刻自动触发 # __new__(cls, *args, **kwargs): 在对象实例化时触发,底层主要是申请内存开辟空间后,为__init__(self)对象分配这块内存空间. # self=new返回的内存空间地址 __init__(self)拿到这块地址后执行init方法的操作,执行完后再将这块内存地址分配给实例化的对象变量 # 类中重写__new__()方法,需要添加return super().__new__(cls)语句,返回开辟的内存地址才能正常运行下去 # __init__(self): 在对象初始化时触发,为对象进行初始化操作 创建完空间之后调用的第一个方法(构造方法) # __call__(self, *args, *kwargs): 将对象当作函数调用时会触发 ->对象名() 如果需要将对象当作函数调用时需要手动重写__call__()方法 # __del__(self): delete的缩写,析构魔术方法 python解释器在程序运行结束时会回收本次执行所开辟的所有内存空间 # 在对象内存被删除回收时自动触发__del__(self)方法,当一块内存空间没有了任何引用,就会默认执行__del__()方法 # 1-对象创建后没有引用,会在创建后立即删除,执行__del__()方法 # 2-对象创建后一直使用变量引用,则会在所有代码执行完毕后删除所有对象,执行__del__方法 # 3-对象创建并使用变量引用保存,主动使用del关键字删除变量引用,当对象的所有引用变量都被删除,对象也相当于被删除,执行__del__()方法 # __str__(self): 有点类似于java的toString()方法,对象的变量名打印出来是内存地址,重写__str__()方法后,打印对象变量名会按你定义的方式做返回,在打印对象引用的变量名时自动触发,需要在__str__(self)方法中添加return,return的内容就是打印对象变量名时看到的数据!
class Person:
def __new__(cls, *args, **kwargs):
print('----> __new__()执行')
address = super().__new__(cls)
print('new开辟空间地址:', address) # new开辟空间地址: <__main__.Person object at 0x000001A3B3A757E0>
return address
def __init__(self, name):
self.name = name
print('____> __init__()执行')
print('init初始化地址:', self) # init初始化地址: <__main__.Person object at 0x000001A3B3A757E0>
def __call__(self, *args, **kwargs):
print('当对象被当作函数调用时--->触发call()')
def __del__(self):
print('对象内存地址被回收时触发的__del__()方法!')
def __str__(self):
return f'当前对象引用变量名地址'
p1 = Person('bob')
print('对象p1地址:', p1) # 对象p1地址: <__main__.Person object at 0x000001A3B3A757E0>
p1() # 当对象被当作函数调用时--->触发call()
print(p1) # 当前对象引用变量名地址
class Cat:
type = '猫科猫属动物'
# 对象初始化调用
def __init__(self, name, age, color):
self.name = name
self.age = age
self.color = color
def catch_mouse(self, color, weight):
print(f'{self.name},抓到了{color}颜色,重{weight}kg的老鼠!')
def eat(self, food):
print(f'{self.name}爱吃{food}')
def sleep(self, hour):
if hour < 5:
print(f'{self.name}还没睡够5小时,可以接着睡!')
else:
print(f'可以去抓老鼠了,睡了{hour}个小时了!')
def show_info(self):
print(f'{self.name}是一只{self.color}的猫,今年{self.age}岁了!')
c1 = Cat('旺财', 5, '白色')
c1.catch_mouse('黑色', 5.5) # 旺财,抓到了黑色颜色,重5.5kg的老鼠!
c1.sleep(4) # 旺财还没睡够5小时,可以接着睡!
c1.sleep(6) # 可以去抓老鼠了,睡了6个小时了!
c1.eat('极品猫粮') # 旺财爱吃极品猫粮
c1.show_info() # 旺财是一只白色的猫,今年5岁了!
# 私有化: 在类内将属性私有化,再通过公有的方法进行私有属性的修改(很像Java的getter和setter) # 私有化其实就是封装,通过'__属性名'形式定义属性以确保该私有化属性只能在本类中进行访问,不对外提供 # 如果想得到和修改私有化属性,我们可以定义公共方法供外界使用(get - set方法)class Student: # 通过init初始化方法在对象内存空间中定义私有化属性 def __init__(self, name, age, scores): self.__name = name self.__age = age self.__scores = scores def __str__(self): return f'姓名:{self.__name},年龄:{self.__age},考试成绩:{self.__scores}' s1 = Student('沃得发', '25', 88) print(s1) # 姓名:沃得发,年龄:25,考试成绩:88 # 如果我们想通过对象名直接获取属性的话是不行的,我们需要添加方法(get\set方法) print(s1.name) # 'Student' object has no attribute 'name'
class Student:
# 通过init初始化方法在对象内存空间中定义私有化属性
def __init__(self, name, age, scores):
self.__name = name
self.__age = age
self.__scores = scores
def __str__(self):
return f'姓名:{self.__name},年龄:{self.__age},考试成绩:{self.__scores}'
def get_name(self):
return self.__name
def set_name(self, name):
self.__name = name
def get_age(self):
return self.__age
def set_age(self, age):
self.__age = age
def get_scores(self):
return self.__scores
def set_scores(self, scores):
self.__scores = scores
s2 = Student('王德梅', '26', 59)
print(s2) # 姓名:王德梅,年龄:26,考试成绩:59
# 通过get()方法得到对象的私有属性
print(s2.get_name()) # 王德梅
print(s2.get_age()) # 26
print(s2.get_scores()) # 59
# 通过set()方法修改对象的私有属性
s2.set_name('李得胜')
s2.set_age('36')
s2.set_scores(66)
print(s2) # 姓名:李得胜,年龄:36,考试成绩:66 修改成功!
# 使用get,set方法的好处,可以在修改属性值的时候加入逻辑判断,私有属性不被外界随意修改
*知识延展 # dir(对象) list形式返回对象内部的属性和方法 print(dir(Student)) print(dir(s2)) print(s2.__dir__()) # 如果对象可以调用__dir__()方法,这个方法和dir(对象)作用相同 # 通过dir方法,我们可以在s2对象中找到这三个属性名 '_Student__age', '_Student__name', '_Student__scores', # 这三个属性名就是属性私有化的原因,我们无法通过s2.__age得到这个属性,是因为底层会自动为私有属性名前拼接'_类名' # [_类名 + __age = '_Student__age'],原本的属性名经过底层的修改变成了'_Student__age',可以访问但不建议 -防君子不防小人- print(s2._Student__age) # 36 其实通过这个变形拼接后的名字还是可以访问到的 可以访问但不建议 -防君子不防小人-
# @property功能1: # 前面通过属性的私有化定义,get\set方法得到\修改对象的私有化属性其实已经搞得不丑了,只是没法通过 对象.属性名 的方法直接访问修改罢了 # 那么如果我们想要像普通属性那种形式访问修改的话,应该怎么做呢? 这里就需要使用这个装饰器: @property
class Student:
# 通过init初始化方法在对象内存空间中定义私有化属性
def __init__(self, name, age, scores):
self.__name = name
self.__age = age
self.__scores = scores
def __str__(self):
return f'姓名:{self.__name},年龄:{self.__age},考试成绩:{self.__scores}'
@property
def name(self):
return self.__name
@name.setter
def name(self, name):
self.__name = name
@property
def age(self):
return self.__age
@age.setter
def age(self, age):
if age <= 120:
self.__age = age
else:
print('年龄赋值失败!')
@property
def scores(self):
return self.__scores
@scores.setter
def scores(self, scores):
self.__scores = scores
# 还是相同的类,我们只不过需要在这几个私有属性上面加上我们的装饰器 get()方法需要加 @property私有属性名 set()方法需要加@私有属性名.setter # 这样我们就可以直接通过[对象名.私有属性名]对私有属性进行打印和修改了
s3 = Student('刘彪', '36', 77)
print(s3) # 姓名:刘彪,年龄:36,考试成绩:77
print(s3.name) # 刘彪
print(s3.age) # 36
print(s3.scores) # 77
# 得到私有属性值打印是没什么问题了,试试修改怎么样
s3.name = '张彪'
s3.age = 46
s3.scores = 88
print(s3) # 姓名:张彪,年龄:46,考试成绩:88 修改测试也成功 由此可见 @property装饰器可以实现我们想要的功能!
# 可能有些同学到这里有点疑问了,既然这种方式访问修改和原本没什么区别,那为什么要搞个私有化呢?这个私有化还有什么意义呢? # 这里我就不得不提一嘴了,我们加上@property装饰器只是为了使用起来和之前一样,但我们还可以在get\set方法体种加入逻辑代码,这是非私有化属性不具备的! # 我们可以age判断,如果大于120岁则显示赋值失败试试
s3.age = 125 # 年龄赋值失败! 测试成功!
print(s3) # 姓名:张彪,年龄:46,考试成绩:88
# @property 装饰器功能2: 可以与所定义的属性配合使用,让属性不被修改.变为只读属性.
class PropertyTest:
def __init__(self):
self._name = '张三' # Property 'name' cannot be set
self._age = '25' # Property 'age' cannot be set
@property
def name(self):
return self._name
@property
def age(self):
return self._age
p1 = PropertyTest()
print(p1.name) # 张三
print(p1.age) # 25
p1.name = '李四' # can't set attribute 'name' 这样我们的私有化属性就通过装饰器变成只读属性了!
*知识扩展
# python中,单下划线属性类似于Java中的protected,双下划线类似于Java中的private, # 但是python中的私有属性仍可以通过_ClassName__FileName这种名字访问到,所以python没有真正意义上的私有 # @property最大的好处就是在类中把一个方法变成属性调用,起到既能检查属性,还能用属性的方式来访问该属性的作用。
# 我们在这里要理解两个概念,一个是组合关系(has a)另一个是(is a)继承关系 # 什么是has a呢?(class A has a kind of class B.意为A类是B类的一部分) # 在一个类中,通过创建另一个类的对象,调用其属性和方法完成某种操作或运算,称为组合关系(比如dog has a leg)
class Computer:
def __init__(self, brand, color):
self.brand = brand
self.color = color
def __str__(self):
return f'电脑品牌:{self.brand},电脑颜色:{self.color}'
class Book:
def __init__(self, book_name, book_num):
self.book_name = book_name
self.book_num = book_num
def __str__(self):
return f'书名:{self.book_name},数量:{self.book_num}'
class Student:
def __init__(self, name, computer, book):
self.name = name
self.computer = computer
self.books = []
self.books.append(book)
def borrow_book(self, borrow_book):
for book in self.books:
if book.book_name == borrow_book.book_name:
print(f'已经有<{borrow_book.book_name}>这本书了')
break
else:
self.books.append(borrow_book)
print('没有这本书,现在添加成功!')
def show_books(self):
# 这里的book是传进来的Book对象
for book in self.books:
print(book.book_name)
def __str__(self):
return f'学生:{self.name}的电脑:{self.computer},书本有:{self.books}'
c1 = Computer('dell', '黑金色')
b1 = Book('乌合之众', 2)
b2 = Book('乌合麒麟', 8)
s1 = Student('李得胜', c1, b1)
print(c1) # 电脑品牌:dell,电脑颜色:黑金色
print(b1) # 书名:乌合之众,数量:2
print(b2) # 书名:乌合麒麟,数量:8
print(s1) # 学生:李得胜的电脑:电脑品牌:dell,电脑颜色:黑金色,书本有:[<__main__.Book object at 0x000001C790513E20>]
s1.show_books() # 乌合之众
s1.borrow_book(b2) # 没有这本书,现在添加成功!
s1.show_books()
# 在python中,除了python初始的系统类(str,list,tuple,dict,set..)之外,我们定义的每一个class都可以被python看作是一个自定义类型 # 在上面的例子中,我们s1(学生对象的内存空间)中,包含了传来的b1和c1这两个另外的自定义类型,这种就是组合关系(has a) -> 把另一个类的对象当成属性传递 # 学生类由电脑对象和书本对象组合而成 student has a computer - student has a book
# 什么是is a呢?(Is-a全称class A is a kind of class B.意为A类是B类的一种具体情况) # 如果B类是基类,在B类中定义了很多基础属性和方法,A类作为其中一种情况,可以继承B类再添加自己的独有方法(比如Apple is a Fruit) # 继承:一个类可以继承另一个类里的所有属性和方法,比如手机需要更新,设计师会直接继承老手机的设计图,在这个设计图上修修改改添加新功能.这就是一种继承! # 当子类对象调用属性或方法时,因为在本类中没有找到,所以会往上去父类中找.
# 单继承: class 子类(父类) # 作为2023年新产品的华威手机,我们加入了5G通话功能,在设计之初,我们不用将4G也取消掉,直接拿之前设计图来用就可以了
class Phone:
producer = '华威'
def __init__(self, brand, color, price):
self.brand = brand
self.color = color
self.price = price
def __str__(self):
return f'{self.brand}:{self.color}:{self.price}'
def call_4g(self):
print('4G通话功能!')
class Phone2023(Phone):
def call_5g(self):
print('5G通话功能!')
phone23 = Phone2023('华威', '远峰蓝', 9999)
print(phone23) # 华为:远峰蓝:9999
phone23.call_4g() # 4G通话功能!
phone23.call_5g() # 5G通话功能!
# 在python中不支持重载,但可以多继承 # 多继承: class 子类(父类1,父类2...) 如果多个父类定义了同名属性,则谁先被继承(谁在左边)谁的优先级高 # 那么作为华威手机,我们还需要添加NFC读卡器功能,目前市面上有设计图纸,我们便一并拿来使用即可.这就是多继承!
class NFCReader:
producer = '小蜜'
def write_NFC(self):
print('写入NFC功能!')
def read_NFC(self):
print('读取NFC功能!')
class Phone23Pro(Phone, NFCReader):
def call_5g(self):
print('5G通话功能!')
phone23pro = Phone23Pro('华威', '远峰蓝', 19999)
print(phone23pro) # 华威:远峰蓝:19999
phone23pro.call_4g() # 4G通话功能!
phone23pro.call_5g() # 5G通话功能!
phone23pro.write_NFC() # 写入NFC功能!
phone23pro.read_NFC() # 读取NFC功能!
print(phone23pro.producer) # 华威 当多继承的父类有同名属性,会继承第一个继承的父类内容 根据继承顺序,从左到右广度优先
# 方法重写与super()父类调用 # 如果子类对继承来的父类中的方法不满意,可以进行复写(在子类中定义同名的属性或方法即可).
class Phone23Mini(Phone):
producer = '华伟'
def call_4g(self):
print('Mini残血芯片运行')
print(f'父类厂商为:{super().producer}')
print(f'本代工厂商为:{self.producer}')
super().call_4g()
print('Mini版本降低续航与通话信号')
phone23mini = Phone23Mini('华威', '黑色', 2999)
print(phone23mini) # 华威:黑色:2999
phone23mini.call_4g() # Mini残血芯片运行 父类厂商为:华威 本代工厂商为:华伟 4G通话功能! Mini版本降低续航与通话信号
# 通过对父类方法的重写,我们可以灵活的对不同业务需求进行修改,父类原本实现的逻辑如果我们想用,直接使用[super()]进行调用使用就可以了.
# 工资管理系统,工人(worker) 销售员(salesman) 经理(manager) 销售经理(sales_manager) # 所有员工(employee)都有工号,姓名,工资,有设置姓名,获取姓名,获取员工号,计算工资等方法 # 工人:工作小时数和时薪属性,工资为工作小时数*时薪 # 销售员:销售额和提成比例属性,工资为销售额*提成比例 # 经理:固定月薪属性,工资为固定月薪 # 销售经理:工资为销售额*提成比例+固定月薪 # 完成功能:添加所有类型成员 计算月薪 显示所有人工资情况
class Employee:
def __init__(self, __id, __name):
self.__id = __id
self.__name = __name
def __str__(self):
return f'员工id:{self.__id},员工姓名:{self.__name}'
# 获取员工号方法
@property
def id(self):
return self.__id
# 获取姓名方法
@property
def name(self):
return self.__name
# 设置姓名方法
@name.setter
def name(self, name):
self.__name = name
# 计算工资方法
def get_salary(self):
pass
class Worker(Employee):
def __init__(self, __id, __name, __hour, __hourly_pay):
super().__init__(__id, __name)
self.__hour = __hour
self.__hourly_pay = __hourly_pay
# 重写计算工资方法
def get_salary(self):
count = self.__hour * self.__hourly_pay * 30
return count
woker1 = Worker('001', '王大树', 8, 10)
# woker1.id = '002' # can't set attribute 'id'
woker1.name = '王树' # 员工id:001,员工姓名:王树
print(woker1) # 员工id:001,员工姓名:王大树
print(woker1.get_salary()) # 80
# 首先使用一个员工基类将这些共同的属性和方法进行封装,随后我们如上,根据不同的用户类进行不同属性和方法的重写与修改就可以