类(class):简单来说就是某一类事粅它们具有相同的属性,例如猫有各种颜色各种颜色就属于属性(也被叫做变量)。
对象(object):黑猫白猫这些都是对象,这个对象就是類的实例(instance)对象/实例只有一种作用,即属性引用
对象内存空间里只存储对象的属性,而不存储方法和静态属性方法和静态属性存儲在类的内存空间中,这样多个对象可以共享类中的资源便于节省内存(见下图)。
实例化:类到对象的过程(实例 = 类名(参数1参数2))
字段(field):对象可以使用属于它的普通变量来存储数据,这种从属于对象或者类的变量叫做字段它们属于某一类的各个实例或对象,或是从属于某一类本身它们被分别称作实例变量(Instance Variables)与类变量(Class
Variables)。有的地方称他们为静态属性和动态属性静态属性针对的是类属性,动态属性针对的是定义在类中的方法
方法(method):对象可以通过类的函数来实现相关功能,这个函数叫做类的方法方法分为普通方法,类方法和静态方法三种方法在内存中都属于类,区别在于调用方式不同详见后面例题。
__init__方法:它会在类的对象被实例化时立即运
role =
'路飞' # 静态属性,对于静态属性的修改必须使用类名直接修改,即类名.静态变量名 #
Person.eat 类名.方法名,查询这个方法的地址
# 实例化的过程:
# 1.创建一个实例/对象,将会作为一个实际参数(ACE) # 2.自动触发一个__init__的方法通过它给对象添加一些属性,对象默认名字是self # 3.执行完__init__方法之后會将self所指向的内存空间返回给实例化它的地方
ACE.beat()
# 对象名.方法名(),这样写相当于方法中的self参数直接指向这个对象 # zoro beated
# 这里对象ACE先找间自己的内存空间,再找见类对象指针根据类对象指针找见person类,再通过类找见beat方法
1.对象的内存空间中只存储对象的属性
而方法和静态属性存储在类的内存涳间中这样即使存在多个方法也只占用类开辟的空间,这样便于节省内存空间
2.对象属性是独有的,静态属性的方法是共享的
3.对象使用洺字先找到自己的命名空间,如果当中存在则优先执行它,而不会通过类对象指针去person类找beat方法
4.对于静态属性的修改必须使用类名修妀(如Person.role = '索隆')
一个类的对象作为另一个类的对象的属性
创建一个圆,以及圆环通过组合,计算圆环的周长与面积(对象名.属性名它表礻另一个对象)
类名.__base__# 类的第一个父类(在讲继承时会讲) 类名.__bases__# 类所有父类构成的元组(在讲继承时会讲)
'''表示有一个带有名字的机器人''' # 一个类变量,用来计数机器人数量 # 当有人被创建时机器人增加数量 '''来自机器人的问候''' # 对于 name 对象变量采用 self.name 标记法加以称呼,这是这个对象中所具有的方法 # 要记住这个类变量与对象变量之间的简单区别。 # how_many 实际上是一个属于类而非属于对象的方法这就意味着我们可以将它定义为一个 #
classmethod(類方法) 或是一个 staticmethod(静态方法) ,这取决于我们是否知道我们需不需 # 要知道我们属于哪个类.
说出打印顺序并解释原因:
4.面向对象的三大特性:继承多态,封装
4.1继承:python 变量中一个新建的类可以继承一个或多个父类
python 变量内继承分为单继承和多继承,基本形式:
# 继承了object类的所囿类都是新式类
如果在开发程序过程中我们已经定义一个类A,现在又定义了一个类B,如果B的类的大部分内容与A都相同则可以使用继承来實现,实现代码的重用
继承中的派生:子类在继承父类的属性基础下,也可以重新定义一些属性如果重新定义的属性(或方法)与父類的相同,则会覆盖父类的属性(或方法)优先执行自己定义的。
# 在单继承中super负责找到当前类所在的父类,这个时候就不需要再手动傳self # 在类内使用super()方法找父类的方法,这样调用子类时可以同时实现父类eat方法 super(Person, mark).eat() # 在外部通过调用super查看person父类中的方法这时候不能用上面的简便方法叻,必须写类名对象名,明确调用者
# 这里使用super方法实现了子类可以调用父类中的属性,通过super可以直接在子类执行父类的方法
看看super的相關用法(很经典)
# 当创建出foochild对象时它会先去Foochild的类中自上而下执行,当遇到init下的super方法时去执行父类的__init__方法,此时打印出parent
接着执行自己嘚方法。当对象调用bar方法时先在foochild中查找,遇到了super方法又返回父类执行父类的bar,接着执行自己的方法以及子类print(self.parent)
看看这个例子(调用了孓类方法的同时又嵌套了父类方法):
多继承:遵循广度优先遍历顺序
这里的多继承是根据子节点去找的,执行d.func()的时候遇到super去B中去找,BΦ也有Super紧接着会去C,发现C也有super由于广度有限,此时它会找到A执行A,再返回C执行C,再到B再到D
# 这里你可以分别把B和C中的super注掉再看看咜的打印
注意:这里会先去F3下,因为存在super会到F2下打印f1,再返回F3去执行f1这个跟上面一样,它是根据节点去查找的
总结:(py3中全是新式类(默认都带有object)遵循广度优先。py2中不带object的属于经典类遵循深度优先)
在单继承中就是单纯的寻找父类 在多继承中就是根据子节点 所在圖 的 mro顺序找寻下一个类 # 找到这个对象对应的类 # 将这个类的所有父类都找到画成一个图 # 根据图写出广度优先的顺序 # 再看代码,看代码的时候偠根据广度优先顺序图来找对应的super
4.2多态(指的是一类事物有多种形态记住,python 变量是自带多态效果的且崇尚鸭子类型)
先来闲聊下python 变量編程原则:
- 开放封闭原则:对于扩展是开放的,对于修改是封闭的
- 接口隔离原则(python 变量里没有接口类的概念java类没有多继承,但是可以通過接口实现多继承)
python 变量中接口类和抽象类:
接口类(在抽象类的基础上):python 变量默认没有接口类接口类不能被实例化,这也意味著接口类中的方法不能被实现
抽象类:python 变量中,默认是有的
父类的方法子类必须实现
(抽象类)父类的方法也可以被实现
相同:这两者都是用来约束子类方法的,都不能被实例化
区别:接口类不能实现方法抽象类可以实现方法里媔的内容
只要是抽象类和接口类中被abstractmethod装饰的方法,都需要被子类实现
当多个类之间有相同的功能也有不同的功能的時候,python 变量应采用多个接口类来实现
如果工作中遇到接口类,记住按照抽象类中的规范去去一一实现对应的方法
多态(通过继承实现):
在一个类之下发展出来的多个类的对象都可以作为参数传入一个函数或者方法在python 变量中不需要可以实现多态,因为它本身自带多态效果
Pyhon不支持Java和C#这一类强类型语言中多态的写法但支持原生多态,其python 变量崇尚“鸭子类型所谓的鸭子类型,说白了就是兩个不同的类拥有相同的方法名利用一个统一的接口来调用,不同的对象在接收时会产生不同的行为(即方法)
鸭子类型:不是通過具体的继承关系来约束某些类必须必须要有哪些方法名是通过一种约定俗成的概念来保证多个类中相似的功能叫相同的名字
# 抽象類和接口类做的事情就是创建规范,制定一个类的metaclass(元类)是ABCmeta这个类就变成了一个接口类,这个类的功能主要就是建立一个规范从而約束后面的子类
# 归一化设计:不管是哪一个类的对象,都调用了同一个函数去实现了相似的功能 c.pay() # 接口类不能被实例化会报错
4.3 封装:顾名思义就是将内容封装到某个地方,以后再去调用被封装在某处的内容
1. 将不需要对外提供的内容都隐藏起来;
1.提高代码的复用性
3.降低代码的冗余度
4.3.1 私有变量和私有方法:
定义一个私有的名字:在私有的名字前面加两条下划线 _ _N = 'name'这样就不能在类的外面调用它
但是,一個私有的名字在存储过程中仍然会出现在A.__dict__中我们仍然可以调用到,只不过python 变量对其名字进行了修改:_类名__名字
在类里面只要代码遇到_ _洺字,就会被python 变量解释器自动转换成 _类_ _名字
# 在类中静态属性,方法对象属性都可以变成私有的,只需要在这些名字之前加上__
e = E() # 报错,因为私有的名字不能被子类继承
# 继承中的关于私有属性的易错点(一不小心就分析错误)
### 子类不能继承父类的私有方法和属性
封装在于奣确区分内外使得类实现者可以修改封装内的东西而不影响外部调用者的代码;而外部使用用者只知道一个接口(函数),只要接口(函数)名、参数不变使用者的代码永远无需改变。
这里我们不要狭隘的认识对象python 变量中一切皆对象,字典也是对象这里也是一种封裝的思想
接下来看看下面的例子:
该类也是一种封装的思想,这里实例化了三次开辟了三个内存空间,不过都是用同一个对象进行調用的
@property(实现了将方法伪装成属性)
能够将一个方法伪装成属性从原来的obj.func()变成了obj.func
被property装饰的方法仍然是一个方法,虽然代码看上去逻輯上没有什么变化但是从调用者角度来看换了一种方式,使之更加合理
# 人体BMI指数 ,体质指数(BMI)=体重(kg)÷身高^2(m)写一个类 描述囚体BMI指数
例:输入原价和折扣价,查看价格的时候可以查看折扣价同时实现可以更换价格,也能实现输出折扣价
@classmethod类方法(可以被类直接調用不需要默认传对象参数,需要传入类参数cls执行类方法时,自动将调用该方法的类复制给cls)
@staticmethod静态方法(由类调用无默认参数)
# 当┅个方法要使用对象的属性时 就是用普通的方法(不带self)
# 当一个方法要使用类中的静态属性时 就是用类方法
# 当一个方法要既不使用对象的屬性也不使用类中的静态属性时,就可以使用staticmethod静态方法
对于实例方法:由实例化对象调用
对于类方法:由于不适用于对象内存空間的属性所以不会将对象和方法绑定到一起,而是将类和方法绑定在一起
对于静态方法:不是绑定方法没有和对象或者类方发生任何绑定关系
""" 定义实例方法,至少有一个self参数 """ """ 定义类方法至少有一个cls参数 """ """ 定义静态方法 ,无默认参数"""
例题:(分析输出的是什么)
(type呮能单纯的判定但是在继承方面判定不出)
7.2.反射(高级知识点,重点)
指程序可以访问监测和修改它本身状态或者行为的一种能力(自渻)
概念:利用字符串的形式去(对象)模块中操作(增删改查)成员。python 变量中一切皆对象都可以使用反射
通过字符串的形式操作对象相關的属性
python 变量中都可以使用反射
# 检查是否存在某属性,也能检查静态属性 # 获取属性,包含静态属性 # 设置属性如果有就改,没有就增 # 删除屬性(如果不存在该属性则报错)无法删除静态属性
# 对于模块,模块使用模块中的名字从自己所在的模块中使用自己名字 # 类使用类命名空間中的名字 # 对象使用对象能用的方法和属性 # 模块使用模块中的名字 # 从自己所在的模块中使用自己名字
类中带下划线的内置方法:
__del__ 析构方法:茬删除一个对象时做一些收尾工作,对象在内存中被释放时自动触发执行用来关闭在对象中打开的系统资源
__init__ 初始化方法:用于初始囮一个新实例,控制这个初始化过程比如添加某些属性
# 不管是在字符串格式化还是打印对象的时候,__repr__方法都可以作为__str__方法的替补 # 一个对潒是否可调用 完全取决于这个对象对应的类是否实现了__call__
再看一个单例模式的例子:
# 每次实例化都返回这同一个对象
其他实现单例模式方式:
#程序中没有调用del方法,在整个程序执行结束之后调用__del__方法 # 调用del方法则先执行析构方法然后执行
__call__:对象后面加括号,触发执行构造方法的执行是由创建对象触发的,即:对象 = 类名() ;而对于__call__
方法的执行是由对象后加括号触发的即:对象() 或者 类()()
有一个类的init方法如下:
我们認为两个对象为同一个对象,已知一个列表中的100个对象对这100个对象进行去重。
### 首先看到这个题会考虑到使用内置函数__eq__方法 ,只要name和sex楿同返回True,用集合去重时会报错说对象时不可哈希类型