python类(class)-面向对象
面型对象的三大特性
- 封装
- 确保对象中的数据安全
- 继承
- 保证了对象的可扩展性
- 多态
- 保证了程序的灵活性
类的封装
封装是面向对象的三大特性之一
封装是指隐藏对象中一些不希望被外部所访问的属性或方法
如果要隐藏一个类型的属性
将对象的属性名,修改为一个外部不知道的名字 (差不多就是自己骗自己这种感觉,外部还是能改的)
如何获取(修改)对象中的属性
需要提供一个getter和setter方法,使外部可以访问到属性
getter 获取对象中指定的属性 (get_属性名)
setter 用来设置对象的指定属性(set_属性名)使用封装确实增加了类的定义复杂程度,但是它也确保了数据的安全性
1.隐藏了属性名,使调用者无法随意的修改对象中的属性
2.增加了getter和setter的方法,很好的控制了属性是否是只读的
3.使用setter方法设置属性,可以增加数据的验证,确保数据的值是正确的的
4.使用getter方法获取属性,使用stter方法设置属性可以在读取属性和修改属性的同时做一些其他的处理
5.使用getter方法可以表示一些计算的属性,比如说在获取属性时,会自动输出一个属性,或者做一些运算等如果希望属性是只读的,则可以直接去掉setter方法
如果希望属性不能被外部访问,则可以直接去掉getter方法
例 # 定义一个狗类 class Dog(): # 定义初始化方法 def __init__(self,name,old,gen): # 在里面定义属性 self.hidden_name = name self.hidden_old = old self.hidden_gen = gen # 定义修改name属性的方法 def set_name(self,name): self.hidden_name = name # 定义查看name属性的方法 def get_name(self): return self.hidden_name # 定义修改old属性的方法 def set_old(self,old): # 年龄没有负数,所以这里要用一个判定如果对方修改的年龄小于0则不作任何操作 if old >= 0 : self.hidden_old = old # 定义查看old属性的方法 def get_old(self): return self.hidden_old # 定义修改gen属性的方法 def set_gen(self,gen): self.hidden_gen = gen # 定义查看gen属性的方法 def get_gen(self): return self.hidden_gen # 定义的通用方法 def jiao(self): print('%s正在叫'%self.hidden_name) def pao(self): print('%s正在跑'%self.hidden_name) def shui(self): print('%s正在睡觉'%self.hidden_name) white = Dog('小白','1','man') white.jiao() white.pao() white.shui() # 这里是white这个对象在调用dog类中的方法,跑,叫,睡觉这些 # 这个是调用方法来修改white的属性,这个属性如果设置不可查看或者不可修改,可以把上面查看和修改的方法去掉 print('修改姓名前',white.get_name()) white.set_name('小黑') print('修改姓名后',white.get_name()) # 输出结果: # 修改姓名前 小白 # 修改姓名后 小黑 # 属性被修改了
类的一些补充
可以为对象的属性使用双下划线开头 __xxx
双下划线开头的属性,是对象的隐藏属性,隐藏属性只能在类的内部访问,无法通过对象访问
双下划线开头的是隐藏属性,隐藏属性只不过是python自动为属性改了一个名字
__属性名 实际上是将名字修改为了
类名.__属性名
这种的其实还是在外部可以改内部的属性值的
类中的装饰器
- property装饰器用来将一个方法,装换为对象的属性
- 添加property装饰器后,我们就可以像调用属性一样,调用方法
- 作用
把方法变为属性,只可以用来访问
class student(): def __init__(self,name,age,score): self.name = name self.age = age self.score = score @property # 加入装饰器后 def get_name(self): return self.name @property # 加入装饰器后 def get_age(self): return self.age @property # 加入装饰器后 def get_course(self): return max(self.score) f1 = student('kkk',17,(100,97,98)) f2 = student('iii',11,(70,60,10)) print(f1.get_name) # 调用方法 print(f1.get_age) # 调用方法 print(f1.get_course) # 加入装饰器后和调用属性差不多,就是调用方法的()没了
- setter装饰器
@属性名.setter装饰器
- 作用
把方法变为属性,对象可以直接赋值来修改
- 这个装饰器是在property下一级的,没有property就不可以用这个装饰器
- (这两个装饰器是一起用的,property的用来访问,setter的用来修改)
# 定义一个类 class Person: # 初始化类的属性 def __init__(self,name): self._name = name # 定义带有property装饰器的方法 @property def get_name(self): return self._name # 定义带有 setter装饰器的方法 (这里的属性名是property装饰器下的方法,因为是修改get_name下的属性) @get_name.setter def set_name(self,name): self._name = name # 创建一个类的实例 并给f1的name属性赋值 f1 = Person('lmk') # 先打印看一下f1的属性值 print(f1.get_name) # 结果是 lmk # 修改 f1的name属性 (通过setter装饰器修改的) f1.set_name = 'kml' # 打印修改后的结果 print(f1.get_name) # 结果是 kml # 这样调用方法就好像调用属性一样, # 但是setter这个装饰器 # self._name = name # name必须加上_让她变成隐藏的属性才能用,要不然会报错
继承
有一个类能够实现我们需要的大部分功能,但是不能实现全部功能
如何让这个类实现全部功能?
- 直接修改这个类,在类中添加我们需要的功能,
修改起来会比较麻烦,并且会违反ocp原则 - 直接创建一个新类,
创建一个新类比较麻烦,并且会出现大量的复制粘贴,出现大量的重复代码 - 直接从一个类中来继承他的属性和方法
- 直接修改这个类,在类中添加我们需要的功能,
继承是面向对象的三大特性之一
通过继承,我们可以使一个类获取到其他类中的属性和方法
在定义类时,可以在类名后的括号中指定当前类的父类,(超类,基类,super)
子类(衍生类)可以直接继承父类中的所有属性和方法
通过继承可以直接让子类获取到父类的方法或属性,避免编写重复性的代码,并且也符合ocp原则,
所以我们经常需要通过继承来对一个类进行扩展
#定义一个父类 class lmk: def run(self): print('动物会跑~~~') def sleep(self): print('动物睡觉~~~') # 定义子类 继承父类 class kml(lmk): def bark(self): print('www') a = kml() a.run() a.sleep() a.bark() # 定义父类后,在用子类继承父类,父类的所有属性和方法,在子类都可以使用,并且在子类中还可以扩展方法, # 在子类的方法,父类是不可以使用的 # 在创建子类时,如果省略不写父类,则默认父类为 object # object是所有类的父类,所有类都继承字object
在创建子类时,如果省略不写父类,则默认父类为 object
object是所有类的父类,所有类都继承字objectissubclass() 检查一个类是否是另一个类的子类
- 参数, 1,子类 2,父类
print(issubclass(kml,lmk)) # 返回结果是 true
- 参数, 1,子类 2,父类
isinstance() 用来检查一个对象是否是一个类的实例
- 参数,1,对象, 类
print(isinstance(a,lmk)) print(isinstance(a,kml)) # 这两个结果都是一样的 # 所有的对象都是object的实例 # print(isinstance(a,object)) # 返回true
- 参数,1,对象, 类
继承-重写
- 如果在子类中有和父类同名的方法,则,通过子类实例去调用方法时,
- 会调用子类的方法,而不是父类的方法,这个特点我们叫做方法的重写,(覆盖,override)
#定义一个父类 class lmk: def run(self): print('动物会跑~~~') def sleep(self): print('动物睡觉~~~') # 定义子类 class kml(lmk): # 在子类添加一个run方法 def run(self): print('动物会跳') def bark(self): print('www') a = kml() # 调用run方法 print(a.run()) # 输出结果 动物会跳 # 如果调用父类的方法,则输出结果还是动物会跑
- 当我们调用一个对象的方法时,
- 会优先去当前对象中寻找是否具有该方法,如果有则直接调用
- 如果没有,则去当前对象的父类中寻找,如果父类中有,则直接调用父类的方法,
- 如果父类没有,则去父类的父类中寻找,以此类推,直到找到object,如果依然没有找到,就报错
子类在调用方法是会优先在当前类中寻找有没有这个方法,没有就往上找
super()
- 子类继承父类,父类的所有方法都会被子类继承,包括特殊方法,也可以重写特殊方法
- super()可以获取当前类的父类
- 并且通过super()返回对象调用父类方法时,不需要传递self
- 格式:
super().__init__(参数1,参数2)
# 定义一个父类 class hello: def __init__(self,name,age): self._name = name self._age = age @property def name(self): return self._name @name.setter def name(self,name): self._name = name @property def age(self): return self._age @name.setter def name(self,age): self._age = age #定义一个子类 class kml(hello): def __init__(self,name,age,hhh): # 通过super()函数接收父类的特殊方法,所以子类在创建对象时,把这个两个参数传递给父类(name,age) super().__init__(name,age) self._hhh = hhh def get_hhh(self): return self.hhh # 创建子类的一个对象 a = kml('kkk',18,'hello') # kkk是父类的name 18是父类的age hello是子类的hhh print(a._name) print(a._age) print(a._hhh)
多重继承
python是支持多重继承的,也就是我们可以为一个类同时指定多个父类
可以在类名()后面添加多个类,实现多重继承
多重继承,会使子类同时拥有多个父类,并且会获取到所有父类中的方法
在开发中如果没有特殊的情况,应该尽量避免使用多重继承,因为多重继承会让我们的代码过于复杂
如果多个父类中有同名的方法,则会在第一个父类中寻找,然后找第二个一次类推
也就是父类中有同名的方法前面的父类会覆盖后面父类的同名方法
类名.__bases__
这个属性会获取当前类的所有父类
print(kml.__bases__) # (<class '__main__.hello'>,) # 父类的顺序是在继承括号中先写那个父类那个父类就是前面的父类
多态
多态是面向对象的三大特性之一
多态从字面理解就是多种形态
就是一个对象可以以不同的形态去呈现
# 定义两个类 class A: def __init__(self,name): self._name = name @property def name(self): return self._name @name.setter def name(self,name): self._name = name class B: def __init__(self,name): self._name = name def __len__(self): return 10 # 通过len()来返回的值,return定什么值,len()函数就返回什么值 @property def name(self): return self._name @name.setter def name(self,name): self._name = name # 定义一个函数 def say_hello(obj): print('你好 %s'%obj.name) # 对于这个函数来说只要对象中含有name属性,它就可以作为参数传递 # 这个函数不会考虑对象的类型,只要有name属性即可 # 再定义一个函数 def say_hello_2(obj): # 做类型检查 if isinstance(obj , A): print('你好 %s'%obj.name) # 在这个函数中我们做了一个类型检查,也就是obj是A类型的对象时,才可以正常使用 # 其他类型的对象都无法使用该函数,这个函数就违反了多态 # 违反了多态的函数,只适用于一种类型的对象,无法处理其他类型的对象,这样导致函数的适应性非常差
上面代码的len()
之所以一个对象能通过len()来获取长度,是因为对象中具有一个特殊方法__len__
换句话说,只要对象中具有__len__特殊方法,就可以通过len()来获取它的长度
这个是输出类中定义对象的len如果打印对象的属性的话该多长还是多长
print(len(b.name))
类中的属性和方法
- 属性
- 类属性
- 实例属性
- 方法
- 类方法
- 实例方法
- 静态方法
类属性,
直接在类中定义的属性
类属性可以通过类或类的实例对象访问到
但是类属性只能通过类本身修改,无法通过实例对象修改,实例对象只能是覆盖类的属性,就是给实例对象重新赋值
实例属性
通过实例对象添加的属性属于实例属性
实例属性只能通过实例对象修改和访问,类对象无法修改
__init__方法是定义实例属性的位置
例如 class kml: pass a = kml() a.age = 19 # 这就是实例属性
实例方法,
在类中定义,以self为第一个参数的方法都是实例方法
实例方法在调用时,python会将调用对象作为self传入
实例方法可以通过实例和类去调用
当通过实例去调用时会自动将当前调用对象作为self传入
当通过类去调用时,不会自动传递self,此时我们必须手动传递self
class kml: def test(self): print('这是test方法~~~ ' , self) a = kml() a.tset() # 通过实例对象调用 kml.test(a) # 通过类调用 必须传入一个实例对象 # a.test() 和 kml.test(a) 是相等的 # 一个是通过实例来调用方法 ,解释器默认传入调用的对象 # 一个是通过类来调用方法并把实例参数传入
类方法
在类内部使用@classmethod来修饰的方法属于类方法
类方法的第一个参数是cls 也会被自动传递,cls就是当前类对象
类方法和实例方法的区别,实例方法的第一个参数是self,而类方法的第一个参数是cls
类方法可以通过类去调用,也可以通过实例去调用,没有区别
class kml: @classmethod def lei(cls): print('这是类方法') kml.lei() a.lei() # 类和对象都可以进行调用
静态方法
在类中使用@staticmethod来修饰的方法数据静态方法
静态方法不需要指定任何的默认参数
静态方法可以通过类和实例去调用
静态方法,基本上就是一个和当前类无关的方法,他只是一个保存到当期类中的函数
静态方法一般都是一些工具方法,和当前类无关
class kml: @staticmethod def jing_tai(): print('这是一个静态方法') kml.jing_tai() a.jing_tai() # 类和对象都可以调用 并且不用传递任何参数 (没有self 没有 cls)
垃圾回收
- 程序在运行过程中会产生垃圾
- 并且会影响程序运行的运行性能,所以这些垃圾必须被清理
- 在程序中没有被引用的对象就是垃圾,所谓垃圾回收就是将垃圾对象从内存中删除
- 在python中有自动的垃圾回收机制,它会自动将这些没有被引用的对象删除
- 所以我们不用手动处理垃圾回收
python的垃圾回收机制
class A:
def __init__(self):
self.name = 'A类'
# del是一个特殊方法,它会在对象被垃圾回收前调用
def __del__(self):
print('A()对象被删除了~~~',self)
a = A()
b = a # 又使用一个变量b,来引用a对应的对象
print(a.name)
input('回车键退出...')
# 回车后python才会进行垃圾回收 在a回收之前会执行 __del__这个特殊方法
# 输出结果 A类
# A()对象被删除了~~~
class A:
def __init__(self):
self.name = 'A类'
# del是一个特殊方法,它会在对象被垃圾回收前调用
def __del__(self):
print('A()对象被删除了~~~',self)
a = A()
b = a # 又使用一个变量b,来引用a对应的对象
print(a.name)
a = None # 将a设置为了None,此时没有任何的变量对A()对象进行引用,它就是变成了垃圾
b = None
# del a
# del b
input('回车键退出...')
# 结果是 程序还在 input阻塞 A类就被垃圾回收了
# 这个实例是 在回车之前A类就被删除了 因为 我们将a这个实例对象设置为了 None
# 这里注释的 del a del b 和 把对象改为 None的效果是一样的
类中垃圾回收的特殊方法
del这个特殊方法,会在对象被垃圾回收前调用
- 调用后,这个空着的对象就会被删除
怎么分辨这个对象是不是垃圾呢
- 如果这个对象有变量去调用它就不是,如果这个变量在后面有重新赋值了,有没有其他变量来引用这个对象,那么就是垃圾
类中的所有特殊方法
特殊方法都是使用__开头和结尾的
特殊方法一般不需要我们手动调用,需要在一些特殊情况下执行
str
__str__(self)
这个特殊方法会在尝试将对象转换为字符串的时候调用
它的作用可以用来指定对象转换为,字符串的结果
比如print(a) a是实例对象
如果不定义__str__ 只会返回父类名称和内存地址
定义了以后会返回你所指定的对象(字符串)
class A: def __str__(self): return '这是一个A类' a = A() print(a) # 打印结果 # 这是一个A类
repr
__repr__(self)
这个方法会在对当前使用repr()函数时调用
它的作用是指定对象在交互界面直接输出结果
class A: def __repr__(self): return '你使用了repr()函数' a = A() print(repr(a)) # 打印结果 # 你使用了repr()函数
str 和 repr
__str__ and __repr__这两个方法都是用于显示的, str是面向用户的 而repr是面向开发人员的
打印类的时候默认是将类转换为str形式
new
new(self)- 是在新式类中出现的方法,就是用来创建对象的 ,一般不用这个变量
运算方法
# object.__add__(self, other) 加 # object.__sub__(self, other) 减 # object.__mul__(self, other) 乘法 # object.__matmul__(self, other) @ # object.__truediv__(self, other) / # object.__floordiv__(self, other) // # object.__mod__(self, other) % # object.__divmod__(self, other) divmod, divmod(a, b) = (a/b, a%b) # object.__pow__(self, other[, modulo]) ** # object.__lshift__(self, other) << # object.__rshift__(self, other) >> # object.__and__(self, other) & # object.__xor__(self, other) ^ # object.__or__(self, other) |
比较方法
# object.__lt__(self, other) 小于 < # object.__le__(self, other) 小于等于 <= # object.__eq__(self, other) 等于 == # object.__ne__(self, other) 不等于 != # object.__gt__(self, other) 大于 > # object.__ge__(self, other) 大于等于 >=
举例__gt__这个方法
gt会在对象做大于比较时调用,该方法的返回值将会作为比较的结果
需要两个参数,一个self表示当前对象,other表示和当前对象比较的对象
也就是 如果是大于的话 就返回前面得参数就大于后面的参数
class A: def __init__(self,name,age): self.name = name self.age = age # 定义gt方法 def __gt__(self,other): # 如果 self 大于 other 返回true 否则返回 false return self.age > other.age # 这里如果写小于的话 就直接返回false 因为 是否大于 # 在啰嗦一遍,创建一个gt方法,如果前面的age>后面的age则就返回true反之就反之就返回false # 创建实例对象 a = A('lmk',18) b = A('kml',16) # 这里调用gt方法,传入要比较的对象 print(a.__gt__(b)) # 输出结果是 true
__len__是获取长度的函数
在类中定义__len__方法,在这个方法内写什么,print(len(对象名或者类名)) 就是什么,因为不是打印的属性
class A: def __init__(self,name): self.name = name # 定义 len方法 def __len__(self): return 10 # 直接返回10 a = A('lmk') # a对象调用 len方法 就直接返回 上面定义的10 print(a.__len__())
__bool__可以通过bool来指定对象转换为布尔值的情况
这个方法可以指定这个类返回的bool值是true还是false
class A: def __init__(self,age): self.age = age def __bool__(self): # 可以用来指定条件,如果 *** 大于 一个数字,则返回true,反之就返回false return self.age > 17 a = A(18) print(a.__bool__()) # 这里 a对象传入了18 大于 类中的17 所以 返回结果是 true 如果小于就返回 false
这些特殊方法可以配合多态进行使用
https://www.jianshu.com/p/882492a9fa99 python 类的运算方法博客可以参考
本博客所有文章是以学习为目的,如果有不对的地方可以一起交流沟通共同学习 邮箱:1248287831@qq.com!