python进阶笔记
面向对象
思想:找人帮我做事
- 面向过程:一步一步亲力亲为
面向对象三大特征:封装性,继承性,多态性
类和对象
函数是一个封装
类也是一个更大封装
类:
- 属性:事物的描述信息
- 行为:事物的行动能力
类-:具有单个或者多个属性或者方法的集合体的统称,是抽象的.
不能直接使用.
类相当于制造飞机的图纸,是负责创建对象
对象:
- 对象是由类创建出的一个具体存在,
- 可以直接使用
- 由哪一个类创建,就具有哪一个类的属性和行为
- 对象就像用图纸造出的飞机
类和对象的关系
- 类是模板,对象是根据类创建出来的
- 先有类,再有对象
- 一个类可以创建多个对象
定义
- 类名用大驼峰法定义(首字母大写)
- 属性--事物的特征
- 方法--事物具有的行为
创建类
class Fun(object) # 定义类名#定义类的属性age = 10 #---实例属性# 定义类的方法def FunCement(self,参数): #默认要有self这是对象的print('行为') #行为的具体表现lo = Fun() # lo是对象,Fun()是调用类,来创建对象lo,此时self是lo
l1 = Fun() # l1是对象,Fun()是调用类,来创建对象l1,此时self是l1
print(Fun.age)
使用类
# 在上面创建好类之后,目前只是将对象给创建出来
# 接下来是使用lo.FunCement()
# 会打印出'行为',也就是使用函数FunCement()
# 函数里面可以传参数
# 其中由于FunCement(self)绑定fo,此时如果FunCement(self)函数里面
# 使用self就表示调用对象自己l1.FunCement()
# 会打印出'行为',也就是使用函数FunCement()
# 函数里面可以传参数
# 其中由于FunCement(self)绑定l1,此时如果FunCement(self)函数里面
# 使用self就表示调用对象自己
__init__的定义
class 类名(object): def __init__(self):print('这里什么时候会被打印?') # 直接创建对象 # 使用对象调用__init__方法
- 在创建对象时,没有调用__init__方法的前提下,就直接把里面的函数输出了
class Cat(object): """模拟猫咪的尝试"""def __init__(self,name, age): self.name = name # 实例属性 self.age = age # 实例属性 def sleep(self): # 实例方法 """猫咪被命令睡觉""" print('%d 岁的 %s猫 正在沙发上睡懒觉' % (self.age, self.name)) def eat(self, food): # 实例方法 """猫咪被命令吃东西""" self.food = foodprint(f'{self.age}岁的{self.name}猫 在吃{self.food}')cat = Cat('Jack', 4) cat.sleep() # -如果在方法中使用self.food,会报错.因为food是在下面 #用eat方法之后才有的传参,进去被定义cat.eat('沙拉')
上面的案例, 创建的每一个实例对象name都叫做Tom, age 都是3岁, 这个是不满足于日常需求的,
通过我们创建的不同实例, 自身的属性都可以不一样, 那么, 我们的写法就得不一样
总结
在创建一个对象的时候, 程序会做两个动作
- 在内存中为对象分配空间
- 调用初始化方法__init__为对象初始化
因此, 通常也会把: 1. 创建出来的对象叫做类的实例 - 创建对象的动作叫做实例化
- 对象的属性叫做实例属性
- 对象调用的方法叫做实例方法
在程序执行时: 1. 对象各自拥有自己的实例属性 - 调用对象方法, 可以通过self.
来访问自己的属性
?函数里面能定义函数吗?
回答:----可以
?注意事项?
class Demo(objet):def __init__(self,name)self.name = name# name = name 是错误的,属性没有绑定对象,
self
由哪一个对象的调用方法,方法内的self就是哪一个对象
- 在封装方法的内部,self就表示当前调用方法的对象自己
- 在调用方法时,程序员不需要传递self参数
- 所谓的self,可以理解为自己
- 某个对象调用方法时,python解释器回吧这个对象作为第一个参数传递给self,所以开发者只需要传递后面的参数即可
init
构造方法,初始化方法
-
类有一个名为__init__的魔法方法,该方法在类的实例化时会自动调用
-
class 类名(Object):def __init__(self):print('一旦实例化就会被调用')f = 类名()
-
在创建对象时,没有调用__init_-方法的前提下,直接把__init里面的代码输出了
class Cat(object):"""模拟猫咪的尝试"""def __init__(self, name, age):self.name = name # 实例属性self.age = age # 实例属性def sleep(self): # 实例方法"""猫咪被命令睡觉"""print('%d 岁的 %s猫 正在沙发上睡懒觉' % (self.age, self.name))def eat(self, food): # 实例方法"""猫咪被命令吃东西"""self.food = foodprint('%d 岁的 %s猫 在吃 %s' % (self.age, self.name, self.food))cat = Cat('Jack', 4) cat.sleep() cat.eat('沙拉')
-
在创建对象时候,程序会做两个动作
- 在内存中为对象分配空间
- 调用初始化方法init为对象初始化
-
总称
- 创建出来的对象叫做类的实例化
- 创建对象的动作叫做实例化
- 对象的属性叫做实例属性
- 对象调用的方法叫做实例方法
-
在程序执行时
- 对象各自拥有自己的实例属性
- 调用对象方法,可以通过self.来访问自己的属性,来调用自己的方法
str方法
class Cat(object):def __init__(self):print('初始化方法')def __str__(self):print('1')return '这是对象返回的内容'cat = Cat()
print(cat)
- 在python中方法名如果是__xxxx__() 的,那么就有特殊的功能,因此叫做“魔法”方法
- 当使用print输出对象的时候,默认打印对象的内存地址。如果类定义了__str__(self) 方
- 法,那么就会打印从在这个方法中 return 的数据
- str 方法通常返回一个字符串,作为这个对象的描述信息
del方法
在对象销毁时自动使用
class Cat(object):def __init__(self):print('初始化方法')def __del__(self):print('什么时候调用?')cat = Cat()
print('*' * 50)
# del cat 加个这个会是怎样的
- 此方法一般无须定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释
放,因为此工作都是交给Python解释器来执行,所以,析构函数的调用是由解释器在进行垃圾回
收时自动触发执行的。 - 当有变量保存了一个对象的引用时,此对象的引用计数就会加1;
当使用del() 删除变量指向的对象时,则会减少对象的引用计数。如果对象的引用计数不为1,那么
会让这个对象的引用计数减1,当对象的引用计数为0的时候,则对象才会被真正删除(内存被回
收)。
私有属性和私有方法
- 之前给大家看到了, 我们可以在类外面, 直接使用对象调用实例属性, 既然可以调用, 就肯定可以修改, 但是这违反了类的封装原则, 因为对象的状态对于类外部应该是不可访问的.
- 为了更好的保存属性安全,即不能随意修改,一般的处理方式为: 将属性定义为私有属性
- 它是以属性命名方式来区分,如果在属性名前面加了2个下划线'__',则表明该属性是私有属性,否则为公有属性(方法也是一样,方法名前面加了2个下划线的话表示该方法是私有的,否则为公有的)。
class Cat(object):"""定义一个Cat类"""def __init__(self, name, age, color='白色'):self.__name = name # 将实例属性变成私有的了self.__age = age # 将实例属性变成私有的了self.color = color # 公有属性def sleep(self): # 实例方法. 公有的"""猫咪被命令睡觉"""print('%d 岁的 %s猫 正在沙发上睡懒觉' % (self.__age, self.__name))def eat(self, food): # 实例方法"""猫咪被命令吃东西"""self.food = foodprint('%d 岁的 %s猫 在吃 %s' % (self.__age, self.__name, self.food))def __run(self):"""猫咪被命令散步"""print('%d 岁的 %s猫 正在散步' % (self.__age, self.__name))
# 实例化对象
其实都是伪私有
# 实例化上面的类
cat = cat('Tom', 3)
print(cat._Cat__name) # 访问私有属性
print(cat._Cat__run) # 访问私有方法
如果就是想要访问私有属性和私有方法, 也是有办法的, 只需要在私有属性或者私有方法前面加一个下划线和类名就可以了 _类名
继承
继承的定义
- 继承的概念:子类 拥有 父类 的所有 方法 和 属性
- 面向对象编程 (OOP) 语言的一个主要功能就是“继承”。继承是指这样一种能力:它可以使用现有类
- 的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展, 实现代码的重用, 相同的
- 代码不需要重复的编写(可以提高开发的效率)
- 通过继承创建的新类称为“子类”或“派生类”。
- 被继承的类称为“基类”、“父类”或“超类”。
class Father(object):def __init__(self, name='马云', age=50):self.__name = name # 私有属性self.age = age # 公有属性def __test(self): # 私有方法print(self.__name) # 打印私有属性print(self.age)def test(self):print(self.__name) # 打印私有属性print(self.age)
class Son(Father):def demo1(self):# print(self.__name) #不能访问到父类的私有属性print(self.age)def demo2(self):# self.__test() #不能访问父类中的私有方法self.test()ft = Father()
# print(ft.__name) #程序出现异常,不能访问私有属性
print(ft.age)
# ft.__test() #程序出现异常,不能访问私有方法
ft.test()
print("------分割线-----")
son = Son(name="小张", age=18)
son.demo1()
son.demo2()
私有的属性,不能通过对象直接访问,但是可以通过方法访问
私有的方法,不能通过对象直接访问,但是可以通过方法访问
私有的属性、方法,不会被子类继承,也不能被访问
一般情况下,私有的属性、方法都是不对外公布的,往往用来做内部的事情,起到安全的作用
多继承
子类可以拥有多个父类,并且具有所有父类的属性和方法
class Children(Father, Son):
Python 中的 mro —— 方法搜索顺序
Python 中针对 类 提供了一个 内置属性 mro 可以查看 方法 搜索顺序
mro 是 method resolution order ,主要用于 在多继承时判断 方法、属性 的调用
路径
在搜索方法时,是按照 mro 的输出结果 从左至右 的顺序查找的
如果在当前类中 找到方法,就直接执行,不再搜索
如果 没有找到,就查找下一个类 中是否有对应的方法,如果找到,就直接执行,不再搜索
如果找到最后一个类,还没有找到方法,程序报错
提示:开发时,应该尽量避免这种容易产生混淆的情况! —— 如果 父类之间 存在 同名的属性或
者方法,应该 尽量避免 使用多继承
重写父类
当 父类 的方法实现不能满足子类需求时,可以对方法进行 重写
所谓 重写,就是子类中,有一个和父类相同名字的方法,在子类中的方法会覆盖掉父类中同名的
方法
- 覆盖 父类的方法
具体的实现方式,就相当于在 子类中 定义了一个 和父类同名的方法并且实现, 直接写想实现
功能的代码 - 对父类方法进行 扩展
- 在需要的位置使用 super().父类方法 来调用父类方法的执行( 或者父类名().父类方法, 不推
荐 )
代码其他的位置针对子类的需求,编写 子类特有的代码实现
class A(object):def test(self):print('原有的功能代码')
class B(A):def test(self):super().test() # 扩展型重写print('新的功能代码')b = B()
b.test()