八、面向对象
8.1 面向对象基础
面向对象:是将一组具有共同特征的事物,将其抽象出来,作为基类,然后再以类为模板去创建具体的对象。比如学生是一个类,而具体到某位同学则是对象,他们具有共同的学生类特征。
8.1.1 类和对象的定义示例:
'''
面向对象:
程序 vs 现实
对象 ----> 具体的事物
现实中事物 --> 转化为程序 : 世间万物皆对象
好处:
类
对象
属性
方法
对象:
xxx的手机
对象的集合 --> 共同点:
动作:打电话、发短信、上网.... --> 方法
特征:品牌、颜色、大小、价格... --> 属性
类别:手机类
多个对象 --》 提取对象的共同特征和动作 --》封装到一个类中
'''
#类名要求首字母大写,多个单词用驼峰式 ValueError,
#定义类和属性
#定义类
class Student():
#类属性
name = 'jack'
age = 10
#使用类来创建对象 对象 = 类名()
jack = Student()
jack.age = 18 #对象属性
print(jack.age) #先找自己空间的,如果没有,然后再去类中查找
print(jack.name)
jack.gender = 'Male'
8.1.2 构造器
类中的方法:动作
'''
类中中的方法:动作,
种类:普通方法(函数),类方法,静态方法,魔术方法
普通方法格式:
def 方法名(self,[选填参数]):
pass
类方法:
class 类名:
def 方法名(self):
pass
'''
#
class Phone(): #默认继承object
#属性
brand = 'Huawei'
price = 5399
type = 'Mate40Pro'
#方法
def call(self): #self 获取到的是函数内存地址
print('self---->',self) #self----> <__main__.Phone object at 0x0000023B8DC9A6A0>,基于函数调用而不断变化
print('正在访问通讯录')
for person in self.address_book: #phone2没有定义address_book,调用则会报错,这里是不能保证每个self中都存在address_book
print(person.items())
print('正在打电话……')
print('留言:',self.note) #不能保证每个self中都存在note
phone1 = Phone()
phone1.note = '我是phone1 的note'
phone1.address_book = [{'13888889999':'Jack'},{'13888889991':'Lucy'}]
print(phone1.brand)
print(phone1,'*****1*****') #<__main__.Phone object at 0x0000023B8DC9A6A0> *****1*****
phone1.call() #call(phone1) --> self.note
phone2 = Phone()
phone2.note = '我是phone2 的note'
print(phone2.brand)
print(phone2,'******2****') #<__main__.Phone object at 0x0000024314FDA630> *****2*****
phone2.call() #call(phone2) --> self.note
8.1.3 初始化对象
在类中定义魔术方法,对象调用时动态改变其参数。
# 类中的方法
class Phone(): #默认继承object
#魔术方法之一,称作魔术方法:__名字__()
def __init__(self): #初始化
print('----------------init')
#动态的给self空间添加2个属性brand 和 price
self.brand = 'Huawei'
self.price = 3999
def call(self): #self不断发生变化
print('---------->call')
print('---------->price:',self.price) #不能保证每个self都有price
'''
1,p先去内存中找有没有一块空间叫Phone;
2,利用Phone类,向内存申请一块跟Phone一样的空间;
3,去Phone中去找方法__init__
如果没有,则执行将开辟的内存给对象p
如果有,则进入__init__方法,执行里面的动作,这里self得到的地址是p的地址,执行完成后,将地址赋值给对象p
'''
p = Phone() #
p.price = 3000 #可修改初始值
p.call() #p.call() p是对象
p1 = Phone()
p1.call()
8.1.4 对象方法
class Person:
name = 'Forrest'
# #不带参数
# def __init__(self): #保证每个对象空间都有 name 和age
# self.name = 'Lucy'
# self.age = 18
#带参数
def __init__(self,name,age): #保证每个对象空间都有 name 和age
self.name = name
self.age = age
#不带参数
# def eat(self): #公共的动作,函数调用才不会有阴影
# print(f'{self.name} is eating Apple')
#带参数
def eat(self,food): #公共的动作,函数调用才不会有阴影
print(f'{self.name} is eating {food}')
def run(self): #self.gender不是公共动作,pycharm报阴影
print(f'{self.name}正在跑步,{self.age}岁了,性别:{self.gender}')
#创建对象p
p = Person('唐力',25) #这里传的参数给init
p.name = 'Tony'
p.eat('Orange') #这个给对应的函数
p.gender = 'Female'
p.run()
#创建对象p1
p1 = Person('摩卡',18)
p1.name = 'Mobi'
p1.eat('Peach')
8.1.5 类方法
8.1.5.1 基础方法
'''
类方法
特点:
1,定义需要依赖装饰器@classmethod
2,类方法中的参数不是一个对象,而是类
print(cls) #<class '__main__.Dog'> 这里打印的是Dog类
3,类方法中只可以使用类属性
4,类方法中是否可以使用普通方法? --> 不能
作用:
1,因为只能访问类属性和类方法,所以可以在对象创建之前,如果需要去完成一些动作/功能,可以将器放在类方法中
2,不依赖于对象
'''
class Dog:
def __init__(self,nickname):
self.nickname = nickname
def run(self): # self 对象去调用
print(f'{self.nickname}在院子里跑来跑去……')
def eat(self):
print(f'{self.nickname} is eating delicious food……')
self.run() #类方法中的调用,需要通过 self.func()
#装饰器
@classmethod
def test(cls): #cls class 类去调用
print(cls) #<class '__main__.Dog'> 这里打印的是Dog类
# print(cls.nickname) # 类里没有定义nickname,报错AttributeError: type object 'Dog' has no attribute 'nickname'
d = Dog('Penny')
d.run()
d.test() #会出错
8.1.5.2 补充方法
'''
类方法:补充方法
'''
class Person:
# age = 18 #类里公共的,外部可以访问调用
__age = 18 #加__ 私有化,只能在类内部调用
def show(self):
print('---------->',Person.__age)
@classmethod
def update_age(cls):
cls.__age = 20
print('----》类方法')
@classmethod
def show_age(cls):
print('修改后的年龄是:',cls.__age)
#直接调用类,不依赖于对象
Person.update_age()
Person.show_age()
8.1.6 静态方法
'''
静态方法:类似于类方法
1,需要装饰器 @staticmethod
2,静态方法无须传递任何参数(self,cls)
3,只能访问类的属性和方法,对象的方法无法访问
4,加载时机同类方法
'''
class Person:
__age = 18 #加__ 私有化,只能在类内部调用
def __init__(self,name):
self.name = name
def show(self):
print('---------->',Person.__age)
@classmethod
def update_age(cls):
cls.__age = 20
print('----》类方法')
@classmethod
def show_age(cls):
print('修改后的年龄是:',cls.__age)
@staticmethod
def test():
print('---->静态方法')
# print('--->',self.name) #语法错误,没有self
print(f'--->',Person.__age) # 只能通过类来调用
#直接调用类,不依赖于对象
Person.update_age()
Person.show_age()
Person('Jack').test() #调用静态方法
8.1.7 总结
类方法 和 静态方法
不同:
1,装饰器不同
2,参数不同,类方法有参数,静态方法无参数
相同:
1,只能访问类的属性和方法,对象的方法无法访问
2,都可以通过类名访问
3,都可以在创建对象之前使用,因为都不依赖于对象
普通方法与两者的区别
不同:
1,没有装饰器
2,普通方法要依赖于对象,因为每个普通方法都有self,self 表示对象本身
3,只有创建了对象,才可以调用普通方法,否则无法调用
8.2 魔术方法
魔术方法是一个对象/类中的方法,和普通方法的唯一不同是:普通方法需要调用,而魔术方法是在特定的时刻自动触发。
'''
魔术发方法:
1, __init__, 初始化,
触发时机:初始化对象时触发,(不是实例化触发,但是和实例化在一个操作中)
2,__new__ 实例化的魔术方法
触发时机:实例化对象时触发
3,__call__ 对象调用方法
触发时机:把对象当函数调用时触发,会默认调用此函数中的内容
4,__del__虚构魔术方法, delete的 缩写,使用默认,建议不要自己写
触发时机:当对象没有用的时候触发。(没有任何变量引用 )
1,对象赋值
p = Person('Jack')
p1 = p
p2 = p
说明:p,p1,p2指向同一个地址
2,删除地址引用
del p2 删除p2对地址的引用
3,查看对地址的引用次数
sys.getrefcount(p)
4,当一块空间没有变量引用,默认执行__del__ 动作, ref = 0
程序运行到最后,也会默认执行__del__ 动作,释放所有的内存占用,类似垃圾回收机制
--》python解释器,回收所有在本次执行过程用到的空间,这是pyhton底层自带的
'''
import sys
class Person:
def __init__(self,name):
print('---------> init',self) #0x0000026751CB2588
self.name = name
def __new__(cls, *args, **kwargs): #向内存要空间 --》 内存地址
print('---------> new')
position = object.__new__(cls) # 获取到内存地址
print(position) #<__main__.Person object at 0x0000026751CB2588>
return position #地址
# return super(Person, cls).__new__(cls)
#把对象当函数调用时执行
def __call__(self, name):
print('---------->call')
print('得到的参数是:',name)
def __del__(self):
print('----------del-----------')
p = Person('Jack')
#程序先执行__new__, 然后执行__init__
print(p) #<__main__.Person object at 0x0000026751CB2588>,不加str方法,打印的时地址
#对象当函数调用,必须要重新__call__函数
p('Jerry')
#del的引用
p1 =p
p2 = p
print(p1.name)
print(p2.name)
p.name = 'Tom'
del p2
print('删除p2后:',p.name)
del p1
print('删除p1后:',p.name)
#查看p的引用次数
del p
#p已经彻底被删除,这时调用__del__,然后再执行下面的代码,如果没有del p,会在打印print(n) 之后再执行__del__
# print(sys.getrefcount(p))
n = 2
print(n)
总结
'''
魔术发方法:
5,__str__ , 触发时机:打印对象名时,自动触发去调用__str__里的内容
单纯打印对象名称,出来的是一个内存地址,这对开发者意义不大
如果想打印对象名时,给开发者更多的信息量
注意:一定要在方法中添加return,return之后的内容,就是打印对象看到的内容
'''
import sys
class Person:
def __init__(self,name,age):
# print('---------> init',self)
self.name = name
self.age = age
def __str__(self):
return '姓名:'+ self.name + '年龄:' + str(self.age)
p = Person('Jack',20)
#程序先执行__new__, 然后执行__init__
print(p) #<__main__.Person object at 0x0000026751CB2588>,不加str方法,打印的时地址,加之后,打印Jack
'''
总结:魔术方法
重点:
__init__ 构造方法,创建完空间之后调用的第一个方法
__str__ 打印对象更多信息
了解:
__new__ 作用:开辟地址空间,建议不需要去重写
__del__ 作用:没有指针引用时,回收地址空间,建议不需要去重写
__call__ 作用:对象当函数使用,根据需要来决定是否重写
'''
8.3 面向对象编程
#猫类
class Cat:
type = 'cat'
#初始化特征
def __init__(self,nickname,age,color):
self.nickname = nickname
self.age = age
self.color = color
def eat(self,food):
print(f'{self.nickname} like to eat {food}')
def catch_mouse(self,color,weight):
print(f'{self.nickname} caught one mouse which is {color} and {weight}kg ')
def cat_sleep(self,hours):
if hours < 5:
print('Please keep sleeping')
else:
print('Get up quickly to catch mouse')
def cat_show(self):
print('Show the details of the Cat')
print(self.nickname,self.age,self.color)
#创建对象
cat1 = Cat('Jerry',2,'White')
#通过对象去调用方法
cat1.catch_mouse('black',2)
cat1.cat_sleep(8)
cat1.eat('golden fish')
cat1.cat_show()
原创文章,作者:kepupublish,如若转载,请注明出处:https://blog.ytso.com/tech/python/273614.html