一、面向对象简介
1. 编程思想
- 总体上的编程思想有2种,一个是面向对象的编程思想,一个是面向过程的编程思想。
面向对象编程:Object Oriented Programming,简称OOP,是一种程序设计思想。
实际上,能够使用面向对象编程思想实现的程序,也都能通过面向过程完成。只是看哪种思想更适合当前开发需求。
- 面向过程与面向对象区别
面向过程:根据业务逻辑从上到下写代码,符合一般的逻辑思维顺序。
面向对象:将数据与函数绑定到一起,进行封装。减少重复代码的重写过程。
2. 面向对象概念及术语
• 类(Class): 用来描述具有相同属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法。其中的对象被称作类的**实例。
• 对象:也称实例。通过类定义的初始化方法,赋予具体的值,成为一个”有血有肉的实体”。
• 实例化:创建类的实例的过程或操作。类似于函数的调用,然后赋值给一个新的变量,就是一个实例化的过程。
• 实例变量:定义在实例中的变量,只作用于当前实例。
• 类变量:类变量是所有实例公有的变量。类变量定义在类中,但在方法体之外。
• 数据成员:类变量、实例变量、方法、类方法、静态方法和属性等的统称。
• 方法:类中定义的函数。
• 静态方法:不需要实例化就可以由类执行的方法
• 类方法:类方法是将类本身作为对象进行操作的方法。
• 方法重写:如果从父类继承的方法不能满足子类的需求,可以对父类的方法进行改写,这个过程也称override。
• 封装:将内部实现包裹起来,对外透明,提供api接口进行调用的机制
• 继承:即一个派生类(derived class)继承父类(base class)的变量和方法。
• 多态:**根据对象类型的不同以不同的方式进行处理。
1. 类与对象介绍
- 类
类是抽象的概念,仅仅是模板。用来描述具有相同属性和方法的对象的集合。比如:”人”是一个类。
- 对象
某一个具体事物的存在 ,在现实世界中可以是看得见摸得着的。 比如:”胡歌”就是一个对象。
类与对象的关系,我们可以进行对象归类。比如:分析班级同学公有特征归类。
点击查看【processon】
2. 类的构成
类由3个部分构成
• 类的名称:类名
• 类的属性:一组数据
• 类的方法:允许对类进行操作的方法
注意:类名通常采用驼峰式命名方式,尽量让字面意思体现出类的作用。
3. 类的表达式:Python使用class关键字来定义类,其基本结构如下:
class 类名:pass
3. 创建对象
python中,可以根据已经定义的类去创建出一个个对象,创建对象的格式为 :
对象名 = 类名() #这又是一个实例化过程,对象名也叫做实例
# 定义class 类名:def 方法名(self,参数): # 类中函数:称为方法pass# 执行s = 类名() # 创建对象(实例) 整个过程就是实例化s.方法名(参数) # 调用类中方法
1. self参数
在类当中定义方法时,会发现系统帮我们自动创建了self参数,并且在调用对象的该方法时,也无需传入self参数。那这个self是什么?实际上,我们需要明确self的两个概念
• self本身是形参
• self就是对象本身
- 练习:定义类为学生类,创建对象李四,在类中定义方法打印李四信息
class Student:def info(self):print(ls.name,ls.age)ls = Student()ls.name = "李四"ls.age = 18ls.info()#out李四 18
class LogicStudent: #类名def test(self): #方法名print(self) #类的内部函数叫做方法ls = LogicStudent() #ls叫做实例/对象,这个过程叫做实例化ls.test() #通过 对象名称.方法名(参数) 进行调用print(ls)print('-'*100)zs =LogicStudent() #当对象/实例是谁,self就是谁zs.test()print(zs)#out<__main__.LogicStudent object at 0x0000000001EAA208><__main__.LogicStudent object at 0x0000000001EAA208>----------------------------------------------------------------------------------------------------<__main__.LogicStudent object at 0x0000000001EAABE0><__main__.LogicStudent object at 0x0000000001EAABE0>
在这个过程当中,对象将自身信息在类的外部定义赋值,封装到了类的内部。
点击查看【processon】
当我再想创建一个对象,并且打印出对象信息时,发现如果这样做不灵活。
class LogicStudent:def test(self):print(ls.name,ls.age)ls = LogicStudent()ls.name = '李四'ls.age = 18ls.test()print(ls)print('-'*100)zs =LogicStudent()zs.name = '张三'zs.age = 20zs.test()print(zs)#out李四 18<__main__.LogicStudent object at 0x00000000021EA208>----------------------------------------------------------------------------------------------------李四 18<__main__.LogicStudent object at 0x00000000021EAA58>
所以优化如下:
class LogicStudent:def test(self):print(self.name,self.age)ls = LogicStudent()ls.name = '李四'ls.age = 20ls.test()print(ls)print('-'*100)zs =LogicStudent()zs.name = '张三'zs.age = 20zs.test()print(zs)#out李四 20<__main__.LogicStudent object at 0x00000000026AA208>----------------------------------------------------------------------------------------------------张三 20<__main__.LogicStudent object at 0x00000000026AAA58>
但是这样仍然有缺陷,大家会发现,用户信息暴露在类的外部。
2. init()方法
init()方法称为初始化方法,也可称为构造方法。在创建对象时,会自动执行该方法,为对象的属性设置初始值。
init()方法不能自定义返回值
init()方法中声明的属性是允许在类的外部进行访问的
class Student():def __init__(self,name,age):self.name = nameself.age = agedef test(self):print(self.name,self.age)self.gender = 'female't = Student('张三',18) #实例化类,实际上t-->selft.test()print(t.name) #既然t-->self,那么t.name -->self.name,从而实现了访问print(t.age)print(t.gender)#out张三 18张三18female
class Student():def __init__(self):print('1')t = Student()print('2')#out12
上题优化代码如下:
class Student:def __init__(self,name,age): #形参接收self.name = nameself.age = agedef test(self):print(self.name,self.age)ls = Student("张三",18) #实参传入ls.test()#out张三 18
3. str()方法
如果在开发中,希望打印输出对象变量时,能够打印自定义的内容。就可以使用str()方法,将自定义内容通过return关键字返回。
class Student:def __init__(self,name,age):self.name=nameself.age=agedef __str__(self):return f'{self.name},{str(self.age)}'wei =Student('wei',18)print(wei)wang =Student('wangwu',22)print(wang)print(id(wei))print(id(wang))print(id(Student))#outwei,18wangwu,22388192643881932037563656
- 注意:返回值必须是字符串。
4. 私有属性与私有方法
1. 私有属性定义
- 私有属性:就是对象不希望公开的属性
- 定义方式:在属性名前面增加两个下划线(例如:__name)
- 练习
• 定义类为:”人”类 • 创建对象:rose • 初始化对象属性:name与age • 要求:age不能在类的外部访问
class Person:def __init__(self):self.name = 'rose'self.__age = 18a = Person()print (a.name)print (a.age)#outAttributeError: 'Person' object has no attribute 'age'rose
2. 私有属性访问
那如果有需求一定要在类的外部访问到对象的私有属性时,我们可以使用如下步骤:
- 可以通过类的内部的方法访问私有属性
- 通过类的内部方法将私有属性返回出去
- 在类的外部调用该方法并且接收就可访问
class Person:def __init__(self):self.name = 'rose'self.__age = 18def test(self):return self.__agea = Person()print (a.name)a_age = a.test()print(a_age)#outrose18
3. 私有方法定义
- 私有方法:就是对象不希望公开的方法
定义方式:在方法名前面增加两个下划线(例如:__test),代码如下:
class Test:def tes1(self):print("--1--")def __tes2(self):print("--2--")def tes3(self):print("--3--")
[x] 注意
在Python中,并没有真正意义上的私有,只有伪私有。当我们在访问时,以对象.类名_私有属性名称即可访问,私有方法同理。代码如下:
class Test:def __init__(self,name,age):self.name=nameself.__age=agewei=Test('wei',22)print(wei.name)print(wei._Test__age)print(id(wei))print(id(wei.name))print(id(wei._Test__age))#outwei223547625635453056505704576
同样也需要注意的是,在实际开发中,不要通过该方式访问对象的私有属性或者私有方法。
- 玩个特别无聊的游戏,猜数字。 玩家输入一个数字与 计算机随机生成的数字作对比,当两个值相等时,则说明用户猜对了。注意:外部不能获取到计算机随机生成的值。
```python
import random
class GuessNum:
def init(self):
def test(self):self.__num = random.randint(1,6)
t=GuessNum() t.test()test_num = int(input("请输入:"))while True:if self.__num == test_num:print("恭喜你 猜对了")breakelse:test_num = int(input("你猜错了,请重新输入:"))
out
请输入:1 你猜错了,请重新输入:2 你猜错了,请重新输入:3 你猜错了,请重新输入:4 你猜错了,请重新输入:5 你猜错了,请重新输入:6 恭喜你 猜对了
- [x] 《寻欢作乐》《面纱》《月亮与六便士》《刀锋》...这些书籍的作者都是毛姆(以最节约内存的方式),并且管理员想统计每本书籍阅读的时长。使用面向对象的方式输出毛姆:《书籍名》总共阅读:**小时```pythonimport timeclass BookMan:book_authr='毛姆'def __init__(self,name):self.name=nameself.read_time=0def reading_time(self,num):self.read_time+=numdef print_info(self):print('{}:{},总计阅读时长为:{}小时'.format(BookMan.book_authr,self.name,self.read_time))@classmethoddef set_authr(cls):cls.book_authr='魏先生'print(cls.book_authr)@staticmethoddef get_systime():print(time.strftime("%H:%M:%S",time.localtime()))book1 = BookMan("《刀锋》")book2 = BookMan("《月亮与六便士》")book3 = BookMan("《寻欢作乐》")book4 = BookMan("《面纱》")book1.reading_time(2)book1.reading_time(2)book2.reading_time(2)book3.reading_time(2)book4.reading_time(2)book1.print_info()book4.print_info()BookMan.set_authr()BookMan.get_systime()#out毛姆:《刀锋》,总计阅读时长为:4小时毛姆:《面纱》,总计阅读时长为:2小时魏先生14:42:11
- 创建一个煎饼类,调用烹饪时长的方法累计煎饼状态:如果煎的时间在0-3之间则状态为生的;如果煎的时间在3-5之间,则状态为半生不熟的;如果煎的时间在5-8之间,则状态为全熟的;当时间超过 8 分钟,状态焦了。并且还可以给煎饼添加作料,比如大葱(hhh)、大蒜(hhh)、烤肠等等
class Pancake:def __init__(self):self.cookState='生的'self.cookLevel=0self.addState=[]def __str__(self):return '煎饼的状态{},烤的时间为{},添加的辅料为{}'.format(self.cookState,self.cookLevel,self.addState)def cook(self,cooktime):self.cookLevel +=cooktimeif self.cookLevel>=0 and self.cookLevel<3:self.cookState='生的'elif self.cookLevel>=3 and self.cookLevel<5:self.cookState='半生不熟'elif self.cookLevel>=5 and self.cookLevel<8:self.cookState='全熟'elif self.cookLevel >= 8:self.cookState = '糊了'def add_addState(self,food):self.addState.append(food)pancake = Pancake()pancake.cook(1)pancake.add_addState('洋葱')#out煎饼的状态生的,烤的时间为1,添加的辅料为['洋葱']
5. 销毁对象
对象什么时候销毁:没有被引用时就会自动销毁
import sysa=1class Demo:passd=Demo()dd = dprint(sys.getrefcount(d))del ddprint(sys.getrefcount(d))print(sys.getrefcount(a))#out32132
二、成员
1. 成员简介
类的成员可分为字段、方法以及属性。
2. 字段
1. 定义
上节课我们学习到的属性,更准确来说被称为字段。字段又主要可以分为普通(实例)字段/实例属性与静态(类)字段/类属性。
2. 普通字段
- 普通字段属于对象,保存在对象中,只能通过对象访问。
- 定义:self.字段名称
- 访问:self.字段名称 或 对象.字段名称
- 练习:定义一个省份类,打印输出班级每位同学所在的国家与省份。
class Province:def __init__(self,country,name):self.country=countryself.name=namedef print_info(self):print(self.country,self.name)wei = Province('中国','安徽')wei.print_info()#out中国 安徽
但是这样实现,会有相应的问题。比如说,普通字段保存在对象中,也就是说,当创建一个对象时,就会重新创建self.country以及self.name字段。当存对象的字段值相同时,仍然使用普通字段便会造成我们内存的浪费。
3. 静态字段(类属性)
- 静态字段属于类,保存在类中。在创建时,仅创建一份,并且所有对象都共享静态字段。执行时可以是类访问也可以是对象访问。
- 定义:直接在类中定义
- 访问:类名.字段名称 或 self.字段名称 或 对象.字段名称
- 练习优化:
class Province:country = '中国'def __init__(self,name):self.name=namedef print_info(self):print(self.name,Province.country)wei = Province('安徽')wei.print_info()#out安徽 中国
总结
在实际开发中,如果字段的值是固定的,不随对象的改变而改变。那就使用静态字段(类属性),这样有利于节省内存的消耗。而如果字段的值随对象的改变而改变,就使用普通字段(实例属性)
3. 方法
1. 分类
类的方法主要可分为普通方法(实例方法),类方法,静态方法。
2. 普通方法(实例方法)
普通方法保存在类中,在实例化对象后,一般通过对象调用。第一个参数必须为系统自建参数,默认为self,代指对象本身。注意:self仅仅是变量名。使用其它也可,但最好不要修改。
定义及访问如下:
class Test:def __init__(self):self.name = 'rose'def test(self):print(self.name)def test2(self):self.test() #在类的内部的其它普通方法中,通过self调用d.test() #在类的内部的其它普通方法中,通过对象调用t = Test()t.test()t.test2()
应用场景:当方法中需要使用普通字段(实例对象)时,通常使用普通方法。
3. 静态方法
- 静态方法通过在方法上面添加@staticmethod装饰器定义,保存在类中;
- 静态方法不需要传入self参数,即使传入了self参数也并不像普通方法那样代指对象本身,仅仅是一个普通的形参。
- 静态方法的调用虽然可以通过对象调用,但一般由类直接调用。定义及访问如下:
```python
class Demo:
def init(self):
def test(self):self.name = "rose"self.age = 18
@staticmethod # 通过@staicmethod装饰器将方法转为静态方法 def stat_test(): # 静态方法self不是必须加的,加了也不代表对象print(self.name)
rose = Demo() rose.test() Demo.stat_test() # 一般通过类名调用print("我是静态方法")
class Demo: def init(self): self.name=’rose’ self.age=18 def test(self): print(self.name) @staticmethod def stat_test(t): #这个参数相当于函数的形参 print(‘我是静态方法’) print(t) d=Demo() Demo.stat_test(‘111’) #将参数传入函数类的方法中,也相当于是静态方法的传参
- 应用场景静态方法的参数并不代表对象这一特点,静态方法在类中使用起来更像一个单纯的函数。完全可以将静态方法放在类的外部,当成函数使用。但是放在类中,便于使用与维护。- [ ] **练习:在类中定义一个打印输出当前时间的方法**```pythonimport timedef show_time():print(time.strftime("%H:%M:%S",time.localtime()))show_time()class Test:@staticmethoddef show_time():print(time.strftime("%H:%M:%S", time.localtime()))t=Test()Test.show_time()
4. 类方法
- 类方法通过在方法上面添加@classmethod装饰器,保存在类中;
- 类方法不需要传入self。但有一个系统自建参数为cls,cls代指类本身;
- 类方法一般通过类调用,也可通过对象调用。定义及访问如下
```python
class Demo:
@classmethod # 通过@classmethod装饰器将方法转为静态
def clasmd(cls): # 自建cls参数 代指 当前类
print(cls) # <class '__main__.Demo'>
d = Demo() Demo.clasmd() # 通过 类名 调用
<a name="3DzOz"></a>#### 总结:<br />一般而言<br />• 访问字段:对象.字段名称<br />• 访问方法:对象.方法名称([参数])<a name="Ivnni"></a>### 4. 属性在实际开发中,为了简便,当想访问方法以属性的访问形式时,就可以通过在方法上面添加@property装饰器,达到该效果。<a name="BR5Gn"></a>#### 1. 定义及访问如下```pythonclass Demo:def __init__(self):self.name = "rose"@propertydef test(self):print(self.name)d = Demo()d.test # 通过@property 以 普通属性 形式 访问 普通方法
注意:以上@property只相当于一个只读属性,也就是说,仅能访问test函数的内容。
[x] 练习:需求1.通过传参去改变self.name的值;2.删除掉self.name。 ```python class Demo: def init(self):
self.name = "rose"
@property def test(self): # 只可访问模式
print(self.name)
@test.setter # d.set_test = “jack” def test(self,name):
self.name = nameprint(self.name)
@test.deleter def test(self): # del d.test
del self.nameprint(self.name) # 报错 self.name已被删除
d = Demo() d.test # 通过@property 只读模式
改变self.name的值
d.test = “jack” # 给方法传参 以 赋值方式 会触发@test.setter
删除self.name
del d.test # del 会 触发 @test.deleter
- [x] **练习:实现分页**思路<br />• 用户输入查看的页面<br />• 比如输入1-->[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]<br />• 比如输入2-->[10, 11, 12, 13, 14, 15, 16, 17, 18, 19]<br />• ...<br />面向过程代码```pythonli = [i for i in range(1000)] # 比如一共100页while True:p = int(input("请输入要查看的页码:"))start = (p - 1) * 10end = p * 10print(li[start:end])
需求
• 使用面向对象编程思想实现
• 并且在类中定义start与end方法
• 通过调用start与end方法实现(尽量通过调用属性的方式实现)
class PageGame:def __init__(self,page):try:page1 = int(page) # input默认为str,所以int强转except Exception as e:page1 = 1 # 万一有输入浮点..字母等会报错,多以捕捉异常,并且重新赋值为1self.page = page1 # 将处理后的p赋值给实例属性page@propertydef start(self):page = (self.page-1) * 10return page@propertydef end(self):page = self.page*10return pageli = [i for i in range(1000)]while True:page = int(input("请输入要查看的页码:"))test = PageGame(page)print(li[test.start:test.end])break#out请输入要查看的页码:2[10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
- 类中有一个私有属性,__age。想要在外部可以对其访问,改变,删除 ,使用 property 的 fget,fset,fdel。
class Test(object):def __init__(self):self.__age=18def get_age(self):print(self.__age)return self.__agedef set_age(self,age):self.__age=ageprint(self.__age)def del_age(self):print(self.__age)del self.__ageprint(self.__age)age=property(fget=get_age,fset=set_age,fdel=del_age)t=Test()t.aget.age='20'del t.age
三、面向对象特征
1. 封装
1. 封装简介:
封装是面向对象编程的一大特点,将属性和方法放到类的内部,通过对象访问属性或者方法,隐藏功能的实现细节,也可以设置访问权限。
点击查看【processon】
2. 封装代码演练:
class Student:def __init__(self,name,age):self.name=nameself.age=agedef print_info(self):print(self.name,self.age)w = Student('wei',18)w.print_info()#outwei 18
2. 继承(重点)
1. 继承介绍
继承是一种创建新类的方式,如果子类需要用到父类的属性或者方法时,就可以使用继承。当然,子类也可以提供自己的属性和方法。
2. 继承格式
class Father: #父类 或者超类、基类passclass Sun(Father): #子类 或者派生类pass
注意:在Python中,新建的类可以继承一个或多个父类,也就是说可以单继承,也可以多继承
3. 继承的作用
避免重复造轮子的行为,减少冗余代码
4. 新式类和经典类
在Python2当中类分为新式类和经典类,如果有继承父类object则是新式类,否则为经典类。
但是在Python3当中,全部都是新式类,默认继承object。
[x] 练习:使用代码演练验证py3中的新式类
class Father: #py2中叫做经典类;py3中默认继承object,都为新式类passclass Son(object): #py2中叫做新式类passf=Father()s=Son()print(len(f.__dir__()))print(len(s.__dir__()))#out26 #26个方法26 #26个方法
5. 单继承
子类继承父类,则可以直接享受父类中已经封装好的方法
[ ] 练习:用代码演练实现单继承的方法
class GrandFather(object):def sleep(self):print('GrandFather sleep')class Father(GrandFather):def eat(self):print('Father eat')def drink(self):print('Father drink')class Son(Father):def study(self):print('Son study')s=Son()s.eat()s.study()s.sleep()#outFather eatSon studyGrandFather sleep
小结:
当对象调用方法时,查找顺序先从自身类找,如果自身没找到,则去父类找,父类无,再到父类的父类找,直到object类,若还无,则报错。这也称为深度优先机制。
当子类与父类拥有同名称的方法时,子类对象调用该方法优先执行自身的方法。那么实际上就是子类的方法覆盖父类的方法,也称为重写。
点击查看【processon】
在实际的开发中,遵循开放封闭原则。我们并不会完全的重写父类的方法,而是希望同时实现父类的功能。这时,我们就需要调用父类的方法了,可以通过super()函数实现。
- super()函数
super()函数是用于调用父类(超类)的一个方法。
- 语法
super(type[, object-or-type])
• type—>类
• object-or-type — 类,一般是 self
[x] 练习:继以上练习实现,在Son的sleep方法当中,调用父类的sleep方法。
class GrandFather(object):def sleep(self):print('GrandFather sleep')class Father(GrandFather):def eat(self):print('Father eat')def drink(self):print('Father drink')def sleep(self):print('Father sleep')super().sleep()class Son(Father):def study(self):print('Son study')def sleep(self):print('Son sleep')super().sleep() #或者super(Son,self).sleep() 或者Father(self).sleep()s=Son()s.eat()s.study()s.sleep()#outFather eatSon studySon sleepGrandFather sleep
小结
- 继承 深度优先 先从自己找 找不到则往父类找
- 重写(优先执行子类中的方法)
- self永远是执行该方法的调用者
- super(子类,self)调用父类中的方法(arg) 或者使用父类名.父类中的方法(self,arg),这里也是深度优先
- 注意
- init方法也会继承,同实例方法(普通方法)一致
- 私有属性以及私有方法没有被继承
class GrandFather(object):def __init__(self):print('我是GrandFather中的构造方法')def sleep(self):print('GrandFather sleep')class Father(GrandFather):def __init__(self):print('我是Father中的构造方法')def eat(self):print('Father eat')def drink(self):print('Father drink')def sleep(self):print('Father sleep')super().sleep()class Son(Father):def __init__(self):print('我是Son中的构造方法')def study(self):print('Son study')def sleep(self):print('Son sleep')super().sleep()s=Son()s.eat()s.study()s.sleep()#out我是Son中的构造方法Father eatSon studySon sleepFather sleepGrandFather sleep
class Fa(object):def __init__(self):self.name='wei'self.__age=20def test(self):print('Fa test')def __test2(self):print('Fa __test2')def test3(self):print(self.__age)self.__test2()class Son(Fa):def B_test(self):print(self.__age)s=Son()s.test()s.test3()#outFa test20Fa __test2
6. 多继承
所谓多继承,即子类有多个父类,并且具有它们的特征。遵循左边优先原则:
点击查看【processon】
代码演练如下:
class F(object):def run(self):print('F run')class F1(object):def run(self):print('F1 run')class S(F1,F): #在这个位置进行顺序执行passs=S()s.run()#outF1 run
class GF(object):def run(self):print('GF run')class F(GF):passclass F1(object):def run(self):print('F1 run')class S(F,F1):passs=S()s.run()#outGF run
class GF(object):def run(self):print('GF run')class F(GF):passclass F1(GF):def run(self):print('F1 run')class S(F,F1):passs=S()s.run()#outF1 run
小结:基于C3算法,设计成如下执行顺序
print(S.__mro__)#out(<class '__main__.S'>, <class '__main__.F'>, <class '__main__.F1'>, <class '__main__.GF'>,<class 'object'>)
3. 多态
1. 概念
多态的概念是应用于Java和C#这一类强类型语言中,而Python崇尚”鸭子类型”
动态语言调用实例方法时不检查类型,只要方法存在,参数正确,就可以调用。这就是动态语言的“鸭子类型”,它并不要求严格的继承体系,一个对象只要“看起来像鸭子,走起路来像鸭子”,那它就可以被看做是鸭子。
所谓多态:定义时的类型和运行时的类型不一样,此时就成为多态。2. 多态演练
class Person(object):def print_info(self):print('打印一条信息。。。')class Man(Person):def print_info(self):print('。。。另外一条自我介绍')def print_info(obj):obj.print_info()wei=Person()print_info(wei)wang=Man()print_info(wang)#out打印一条信息。。。。。。另外一条自我介绍
4. 常用魔法方法
1. 魔法方法简介
在Python中,有一些内置好的特定方法,这些方法在进行特定的操作时会自动被调用,称为魔法方法。
魔法方法的命名总是被双下划线包围,比如名称。doc
doc用来查看类的说明文档
[ ] 练习,查看列表类的说明文档
print(list.__doc__)#outlist() -> new empty listlist(iterable) -> new list initialized from iterable's items
查看自定义类的说明文档 ```python class Demo(object): “”” 我是用来检测 类的说明文档的 “”” pass d=Demo() print(d.doc)
out
我是用来检测 类的说明文档的
- **__module__**__module__用来查看当前操作的类所在模块,一个.py文件其实就是一个模块- [ ] 练习:创建A.py与B.py,在A.py中定义类Demo,在A.py中输出Demo.__module__,在B.py中导入A中的Demo类运行B.py- **总结**当执行模块是类所在模块时,执行结果为__main__。否则,执行结果是类所在模块的名称。- **__class__**__class__用来查看当前对象的类- [ ] 练习• 创建Demo类<br />• 创建Demo对象为d<br />• 查看Demo类的__class__<br />• 查看Demo对象d的__class__```pythonclass Demo(object):"""我是用来检测 类的说明文档的"""passd=Demo()print(Demo.__class__)print(d.__class__)#OUT<class 'type'><class '__main__.Demo'>
- 总结
类也是对象,Demo是type类的对象,d是Demo类的对象。
- dict
dict用于获取类或者实例的属性字典
注意
• 普通字段存储在对象中,所以通过对象.dict获取的是普通字段
• 除普通字段以外的成员都存储在类中,所以通过类.dict来获取
class Students(object):def __init__(self):self.name='wei'self.__age=18def test(self):self.gender='male'print('test')wei=Students()print(wei.__dict__)#out{'name': 'wei', '_Students__age': 18}print(Students.__dict__)#OUT{'__module__': '__main__', '__init__': <function Students.__init__ at 0x00000000028A1F28>, 'test': <function Students.test at 0x00000000028AA2F0>, '__dict__': <attribute '__dict__' of 'Students' objects>, '__weakref__': <attribute '__weakref__' of 'Students' objects>, '__doc__': None}
- del()方法
del()方法也叫做析构方法。当由该类创建的实例对象,被删除或者说在内存中被释放,将会自动触发执行。
总结
• 当代码全部执行完毕才自动触发del()
• 如果需要提前触发,则需要通过del关键字,删除所有对象后触发del()
class Demo(object):def __del__(self):print('我被回收了')d=Demo()print('--'*50)print('--'*50)#out--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------我被回收了class Demo(object):def __del__(self):print('我被回收了')d=Demo()print('--'*50)del dprint('--'*50)#out----------------------------------------------------------------------------------------------------我被回收了----------------------------------------------------------------------------------------------------
注意
此方法一般不需要定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配与释放,一般都是交给Python解释器来执行。所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行。
- call()方法
call()方法用于将对象变成一个可调用的对象。也就是说,当一个类中有call()方法时,其实例化得到的对象便是可调用的(callable)
class Demo(object):def __call__(self, *args, **kwargs):print('可被调用了')passd=Demo()print(d)d()#out<__main__.Demo object at 0x0000000001E9F9E8>可被调用了
- new()方法
new()方法用于创建与返回一个对象。在类准备将自身实例化时调用
- 练习:以下代码打印输出的顺序?
A.init,new
B.init
C.new
D.new,init__
class Demo(object):def __init__(self):print('__init__')def __new__(cls, *args, **kwargs):print('__new__')d=Demo()#out__new__
注意
• new()方法用于创建对象
• init()方法在对象创建的时候,自动调用
• 但是此处重写了父类的new()方法,覆盖了父类new()创建对象的功能,所以对象并没有创建成功。所以仅执行new()方法内部代码
- 对象创建执行顺序
• 1.通过new()方法创建对象
• 2.并将对象返回,传给init()
- 练习:在自定义类中实现创建对象
思路
• 重写父类new()方法
• 并且在该方法内部,调用父类的new()方法
class Demo(object):def __init__(self):print('__init__')def __new__(cls, *args, **kwargs):print('__new__')return super().__new__(cls)d=Demo()#out__new____init__
注意
• 在创建对象时,一定要将对象返回,才会自动触发init()方法
• init()方法当中的self,实际上就是new返回的实例,也就是该对象
init()与new()区别
• init实例方法,new静态方法
• init在对象创建后自动调用,new创建对象的方法
2.单例模式
思路
• 当对象不存在时,创建对象
• 当对象存在时,永远返回当前已经创建对象
