python--class基础
(1)创建类(只包含方法)
class 类名: def 方法1(self, 参数列表): pass def 方法2(self, 参数列表): pass
self是必须参数,self代表对象本身
(2)创建对象
对象名 = 类名()
例:小猫叫爱吃鱼,小猫在喝水
class Cat: def eat(self): print('小猫在吃鱼') def drink(self): print('小猫在喝水') little_cat = Cat() little_cat.eat() # 小猫在吃鱼 little_cat.drink() # 小猫在喝水
如果这只小猫有名字,叫Tom,俨然他的名字就是一个属性,此时我们可以修改代码
class Cat: def eat(self): print(f'{self.name}在吃鱼') def drink(self): print(f'{self.name}在喝水') little_cat = Cat()
little_cat.name = 'Tom' # 在类的外面给对象添加属性 ,在类的外面添加属性不推荐
little_cat.eat() # Tom在吃鱼
little_cat.drink() # Tom在喝水
class Cat: def eat(self): print(f'{self.name}在吃鱼') def drink(self): print(f'{self.name}在喝水') little_cat = Cat() little_cat.eat() little_cat.drink()
little_cat.name = 'Tom' # 因顺序原因,会报错
如果将代码修改成上面面这样,就会报错,所以不推荐在类外面给对象添加属性
(3)初始化方法介绍
当使用 类名() 创建对象时,会自动执行以下操作:
1.为对象在内存中分配空间 —— 创建对象(调用_new)
2.为对象的属性 设置初始值 —— 初始化方法 (调用_init并且将第一步创建的对象,通过self参数传给_init_)
这个初始化方法 就是_init_ 方法,_init_ 是对象的内置方
- _init_ 方法是专门用来定义一个类具有哪些属性 ,并且给出这些属性的初始值的方法!
在初始化方法内部定义属性
在 _init_ 方法内部使用 : self.属性名 = 属性的初始值 ,就可以定义属性
定义属性之后,再使用类创建的对象,都会拥有该属性
- 在开发中,如果希望在 创建对象的同时,就设置对象的属性,可以对 _init_ 方法进行改造:
1.把希望设置的属性值,定义成 init 方法的参数
2.在方法内部使用 self.属性 = 形参 , 接收外部传递的参数
3.在创建对象时,使用 类名(属性1, 属性2...) 调用
class Cat: def __init__(self): print('这是一个初始化方法') #当创建对象时会自动调用初始化方法:__init__方法,所以我们可以通过给初始化方法传递参数去给类添加属性,这样在创建对象时会自动初始化他的属性
def eat(self): print(f'{self.name}在吃鱼') def drink(self): print(f'{self.name}在喝水') little_cat = Cat() # 这是一个初始化方法
class Cat: def __init__(self,name): #当创建对象时会自动调用初始化方法:__init__方法,通过给初始化方法传递参数去给类添加属性,这样在创建对象时会自动初始化他的属性
# 对象的属性名=参数名
self.name = name
print(self.name)
def eat(self):
print(f'{self.name}在吃鱼')
def drink(self):
print(f'{self.name}在喝水')
little_cat = Cat('Tom')
little_cat.eat() # Tom在吃鱼 little_cat.drink() # Tom在喝水 big_cat = Cat('Jerry')
big_cat.eat() # Jerry在吃鱼 big_cat.drink() # Jerry在喝水
(4)__del__方法介绍
对象在从内从地址中销毁之前会自动调用此方法
class Cat: def __init__(self, name): self.name = name def eat(self): print(f'{self.name}在吃鱼') def drink(self): print(f'{self.name}在喝水') def __del__(self): print(f'{self.name}跑了') little_cat = Cat('Tom') little_cat.eat() # Tom在吃鱼 little_cat.drink() # Tom在喝水 big_cat = Cat('Jerry') big_cat.eat() # Jerry在吃鱼 big_cat.drink() # Jerry在喝水 print('*' * 50) ''' Tom在吃鱼 Tom在喝水 Jerry在吃鱼 Jerry在喝水 ************************************************** Tom跑了 #自动调用 Jerry跑了 #自动调用 '''
我们发现当程序执行完时,big_cat和little_cat被自动销毁,并且执行了__del__方法 , 我们也可以进行手动删除
class Cat: def __init__(self, name): self.name = name def eat(self): print(f'{self.name}在吃鱼') def drink(self): print(f'{self.name}在喝水') def __del__(self): print(f'{self.name}跑了') little_cat = Cat('Tom') little_cat.eat() # Tom在吃鱼 little_cat.drink() # Tom在喝水 big_cat = Cat('Jerry') big_cat.eat() # Jerry在吃鱼 big_cat.drink() # Jerry在喝水 del little_cat #手动删除 print('*' * 50) ''' Tom在吃鱼 Tom在喝水 Jerry在吃鱼 Jerry在喝水 Tom跑了 ************************************************** Jerry跑了 '''
我们在打印*前手动删除了little_cat变量,他就执行了__del__方法,big_cat在程序执行完被自动销毁,执行__del__方法
(5)__str__方法
在 Python 中,使用 print 输出 对象变量,默认情况下,会输出这个变量的类型,以及 在内存中的地址(十六进制表示)
如果在开发中,希望使用 print 输出 对象变量 时,能够打印 自定义的内容,就可以利用 _str_ 这个内置方法
- 注意:_str_ 方法必须返回一个字符串
class Cat: def __init__(self, name): self.name = name def eat(self): print(f'{self.name}在吃鱼') def drink(self): print(f'{self.name}在喝水') def __del__(self): print(f'{self.name}跑了') little_cat = Cat('Tom')
#我们在加入__str__方法之前,打印对象的变量名,会发现输出的是对象在内存中的地址
print(little_cat) # <__main__.Cat object at 0x000001832B989190>
little_cat.eat() # Tom在吃鱼 little_cat.drink() # Tom在喝水
我们在加入__str__方法之前,打印对象的变量名,会发现输出的是对象在内存中的地址
class Cat: def __init__(self, name): self.name = name def __str__(self): return f'这只猫的名字叫{self.name}' def eat(self): print(f'{self.name}在吃鱼') def drink(self): print(f'{self.name}在喝水') def __del__(self): print(f'{self.name}跑了') little_cat = Cat('Tom')
#当我们设置了__str__方法中return的内容后,再打印对象的变量名,输出就是__str__方法中return的内容
print(little_cat) # 这只猫的名字叫Tom ,这次不打印内存地址,是因为加入了__str__方法return
little_cat.eat() # Tom在吃鱼 little_cat.drink() # Tom在喝水
当我们设置了__str__方法中return的内容后,再打印对象的变量名,输出就是__str__方法中return的内容
4.面向对象的封装
封装: 根据 职责 将 属性 和 方法 封装到一个 抽象的类 中将数据(属性)和行为(方法)包装到类对象中。在方法内部对属性进行操作,在类对象的外部调用方法。这样无需关心方法
- 内部的具体实现,从而隔离了复杂度
- 封装是面向对象编程的一大特点
- 面向对象编程的第一步。将属性和方法封装到一个抽象的类中
- 外界使用类创建对象,然后让对象调用方法
- 对象方法的细节都封装在类的内部
# encoding:utf-8 class Person: def __init__(self, name, weight): self.name = name self.weight = weight print(self.name,self.weight) # 内置函数,可设置print打印输出地址时的特定显示,因此必须要有返回值 def __str__(self): #当我们设置了__str__方法中return的内容后,再打印对象的变量名,输出就是__str__方法中return的内容 return "我是%s,体重是%.1f公斤" % (self.name, self.weight) def run(self): self.weight -= 0.5 print("%s通过跑步体重减少0.5公斤" % self.name) def eat(self): self.weight += 1 print("%s吃太多,体重增加1公斤" % self.name) p1 = Person("小明", 65) print("p1: 此时的状态: ",p1) p1.run() print("调用run后 ,p1: 跑步减肥后的状态: ",p1) p1.eat() print("调用eat后 ,p1: 吃饭增肥后的状态: ",p1) ################################################################################################################################# print('\033[1;33m%s\033[0m' % ("分割线".center(166, '-'))) ################################################################################################################################# p2 = Person("小美", 45) print("p2: 此时的状态: ",p2) p2.eat() print("调用eat后,p2:吃饭增肥的状态",p2) p2.run() print("调用run后,p2:跑步减肥的状态",p2)
注意:在开发时,被使用的类需要先开发 ,因为House类中要用到家具,所以先开发HouseItem类
class HouseItem:
def __init__(self, name, area):
"""
:param name: 家具名称
:param area: 占地面积
"""
self.name = name
self.area = area
# 必须要有返回值, 当我们设置了__str__方法中return的内容后,再打印对象的变量名,输出就是__str__方法中return的内容
def __str__(self):
return '[%s] 占地面积是 %.2f 平米' % (self.name, self.area)
# 创建房子类
class House:
def __init__(self, house_type, area):
self.house_type = house_type
self.area = area
self.free_area = area
self.item_list = []
# 添加家具
def add_item(self, item):
# 判断面积是否足够添加家具
if item.area < self.free_area:
self.item_list.append(item.name)
self.free_area -= item.area
print("添加%s,占用面积%.1f" % (item.name, item.area))
else:
print("面积不足无法添加")
# 输出房子打印
# 必须要有返回值, 当我们设置了__str__方法中return的内容后,再打印对象的变量名,输出就是__str__方法中return的内容
def __str__(self):
# Python可以自动将一对括号内的代码连接到一起
return ("该房子的户型:%s\n总面积为:%.1f平米\n剩余面积为:%.1f平米\n家具:%s"
% (self.house_type, self.area, self.free_area, self.item_list))
# 创建家具对象
jj1 = HouseItem("席梦思", 4)
jj2 = HouseItem("衣柜", 2)
jj3 = HouseItem("餐桌", 1.5)
print(jj1) # 家具席梦思,占地面积为:4.0平米
print(jj2) # 家具衣柜,占地面积为:2.0平米
print(jj3) # 家具餐桌,占地面积为:1.5平米
print("-" * 30)
# 创建房子对象,并添加家具
hs = House("大平层", 6)
hs.add_item(jj1)
hs.add_item(jj2)
hs.add_item(jj3)
print("-" * 30)
print(hs)
5.面向对象的私有属性和私有方法
对象 的 某些属性或方法 只希望 在对象的内部被使用,而 不希望在外部被访问到
定义:在属性名或者在方法名前面加两个下划线
class Girl: def __init__(self, name, age): self.name = name # 定义私有属性 self.__age = age # 定义私有方法 def __secret(self): print(f'{self.name}的年龄是{self.__age}') xiaomei = Girl('小美', 20) print(xiaomei.name) # 小美 print(xiaomei.__age) # 报错 xiaomei = Girl('小美', 20) xiaomei.__secret() # 报错
然而,在python中并没有真正的私有,我们还是有办法访问到私有属性和私有方法
class Girl: def __init__(self, name, age): self.name = name # 定义私有属性 self.__age = age # 定义私有方法 def __secret(self): print(f'{self.name}的年龄是{self.__age}') xiaomei = Girl('小美', 20) print(xiaomei._Girl__age) # 20 #属性前加上 _类名,就可以使用私有属性
我们在调用__age属性前加上 _类名,就可以使用私有属性
class Girl: def __init__(self, name, age): self.name = name # 定义私有属性 self.__age = age # 定义私有方法 def __secret(self): print(f'{self.name}的年龄是{self.__age}') xiaomei = Girl('小美', 20) xiaomei._Girl__secret() # 小美的年龄是20 ,在调用__secret方法前加上 _类名,就可以调用私有方法
在调用__secret方法前加上 _类名,就可以调用私有方法
访问方法
- 1.通过自定义get,set方法提供私有属性的访问
- 2.调用property方法提供私有属性的访问
- 3.使用property标注提供私有属性的访问
class Person: def __init__(self,name,age,speak): self.name = name self.__age = age self.__speak = speak # set,get 方法 @property def age(self): return self.__age @age.setter def age(self,age): if age > 150 or age < 0: print('年龄不合法') return None self.__age = age # age = property(getAge,setAge) #顺序不能改,必须先get后set @property def speak(self): return self.__speak @speak.setter def speak(self, speak): self.__speak = speak zhangsan = Person('zhangsan',88,'Chinese') print(zhangsan.name) zhangsan.age = 155 print(zhangsan.age) zhangsan.speak = 'English' print(zhangsan.speak)
# encoding:utf-8 # 私有属性、私有方法 class Student: def __init__(self, name, age): self.name = name self.__age = age # 使用(__属性名)的方式将age声明为私有属性 def show(self): print(self.name, "的年龄为:", self.__age) # 可以在类的内部使用私有属性 def __show_1(self): # 使用(__方法名)的方式将show_1定义为私有方法 print("这是{}的私有方法".format(self.name)) stu_1 = Student("张三", 20) stu_1.show() # 由此可见可以在类的内部使用私用属性 print(stu_1.name) print(stu_1.__age) #代码报错,因为在类对象中并不能访问类的私有属性 stu_1.__show_1() #代码报错,因为在类对象中并不能访问类的私有属性 """ 在Python中起始并没有真正的私有属性、方法: 给属性、方法命名时,实际是对名称做了一些特殊处理,使得类对象无法直接访问。 但是如果一定要从外界访问私有属性、方法的话,那么只需要在私有属性、方法前加上_类名 例:stu_1._Student__age stu_1._Student__show_1() """ print(stu_1._Student__age) stu_1._Student__show_1()
注意:
子类不能直接继承父类的私有属性和私有方法
子类对象可以调用父类的公有方法,在父类的公有方法中调用父类的私有方法和私有属性,那么子类对象就可以间接的使用该公有方法访问父类的私有属性和私有方法
6.面向对象的继承
继承:子类拥有父类的所有属性和方法
例如:
狗属于动物,动物类可以吃和睡,那么狗类也可以吃和睡,狗既然属于动物类,那么是否有一种办法不用在狗类里面重新定义一个吃和睡的方法吗(避免代码的冗余)?这就要使用到继承
class Animal: def eat(self): print('eat') def sleep(self): print('sleep') # 子类(继承的父类) class Dog(Animal): # 可以添加子类特有的方法 def bark(self): print('bark') dog = Dog() dog.eat() # eat dog.sleep() # sleep dog.bark() # bark
可以看见我们在继承动物类的狗类中并没有定义eat和sleep方法,但可以调用父类(Animal类)中的eat和sleep方法
class Animal: def eat(self): print('eat') def sleep(self): print('sleep') # 子类(继承的父类) class Dog(Animal): # 可以添加子类特有的方法 def bark(self): print('bark') class XiaoTianQuan(Dog): def fly(self): print('fly') dog = XiaoTianQuan() dog.eat() # eat dog.sleep() # sleep dog.bark() # bark dog.fly() # fly
又定义了一个哮天犬类让他继承于狗类,而狗类继承于动物类,我们发现哮天犬类的对象可以调用父类的父类的方法,说明继承具有传递性
方法的重写
当父类中的方法不满足子类的需求时,我们需要怎么办呢?
1.覆盖父类的方法
class Dog(Animal): # 可以添加子类特有的方法 def bark(self): print('bark') class XiaoTianQuan(Dog): def fly(self): print('fly')
class Dog(): # 可以添加子类特有的方法 def bark(self): print('bark') class XiaoTianQuan(Dog): def fly(self): print('fly') def bark(self): print('哮天犬的bark') dog = XiaoTianQuan() dog.bark() # 哮天犬的bark ,先找子类的方法 ,找到旧执行,找不到再去父类找。
我们可以在哮天犬类中再定义一个bark方法,当执行dog.bark()时,会先找子类的bark方法,找到了就执行子类的bark方法,如果子类没有再去父类找bark方法。
这就是方法重写的第一种:覆盖父类的方法
2.扩展父类方法
父类的方法不能完全满足需求(只能满足部分需求),就要在子类中对父类的方法进行扩展,这个如何实现呢?
class Dog(): # 可以添加子类特有的方法 def bark(self): print('Dog类的 bark') class XiaoTianQuan(Dog): def fly(self): print('fly') def bark(self): super().bark() #在子类方法中调用父类方法, 使用super().
方法名 print('哮天犬的bark') dog = XiaoTianQuan() dog.bark()
在子类中也定义一个bark方法,使用 super().父类方法名() 的形式对父类方法名进行调用,然后再增加子类特有的需求,这样最后在创建完对象后,调用bark方法,就会调用子类中拓展完父类bark的bark方法
注意:当父类中有私有方法和私有属性时,子类并不能直接继承父类的私有属性和私有方法,如果想使用就可以使用我们上面介绍的方法。
多继承
一个子类继承于多个父类就叫多继承
class A(object): def test(self): print('A---test') def demo(self): print('A---demo') class B(object): def demo(self): print("B---demo") def test(self): print('B---test') class C(B, A): pass c = C() c.test() # B---test c.demo() # B---demo
注意事项:当子类继承的多个父类中有名字相同的方法,并且我们调用了此方法时,会优先执行括号中第一个父类的该方法
7.多态
不同的子类调用相同的父类方法产生不同的结果叫做多态
前提条件:
1.继承:多态一定是发生在子类和父类之间;
2.重写:子类重写了父类的方法。
class WhoSay: def say(self,who): who.say() class CLanguage: def say(self): print("调用的是 Clanguage 类的say方法") class CPython(CLanguage): def say(self): print("调用的是 CPython 类的say方法") class CLinux(CLanguage): def say(self): print("调用的是 CLinux 类的say方法")
a = WhoSay()
#调用 CLanguage 类的 say() 方法 a.say(CLanguage()) #调用 CPython 类的 say() 方法 a.say(CPython()) #调用 CLinux 类的 say() 方法 a.say(CLinux()) ''' 调用的是 Clanguage 类的say方法 调用的是 CPython 类的say方法 调用的是 CLinux 类的say方法 '''
class Dog(object): def __init__(self, name): self.name = name def game(self): print("蹦蹦跳跳") class GodDog(Dog): def game(self): print("%s在天上飞" % self.name) class ErHa(Dog): def game(self): print("%s这个傻狗在地上滚" % self.name) class Person(object): def __init__(self, name): self.name = name def ame_with_dog(self, dog): print("%s在和%s玩耍" % (self.name, dog.name)) dog.game() # 创建二哈 eh = ErHa("哈士奇") eh.game() # 创建哮天犬 xtq = GodDog("哮天犬") xtq.game() ################################################################################################################################# print('\033[1;33m%s\033[0m' % ("分割线".center(166, '-'))) ################################################################################################################################# # 创建二郎神 els = Person("二郎神") els.ame_with_dog(eh) print("*" *60) els.ame_with_dog(xtq)
当传入的狗对象不同时,代码的运行结果也不相同