面向对象
面向对象编程概念:(Object Oriented Programming-OOP)
是一种解决软件复用的设计和编程方法。 这种方法把软件系统中相近相似的操作逻辑和操作 应用数据、状态,以类的型式描述出来,以对象实例的形式在软件系统中复用,以达到提高软件开发效率的作用。
特点:
- 类(Class): 用来描述具有相同的属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法。对象是类的实例
- 方法:类中定义的函数。
- 类变量:类变量在整个实例化的对象中是公用的。类变量定义在类中且在函数体之外。类变量通常不作为实例变量使用。
- 数据成员:类变量或者实例变量用于处理类及其实例对象的相关的数据。
- 方法重写:如果从父类继承的方法不能满足子类的需求,可以对其进行改写,这个过程叫方法的覆盖(override),也称为方法的重写。
- 局部变量:定义在方法中的变量,只作用于当前实例的类。
- 实例变量:在类的声明中,属性是用变量来表示的,这种变量就称为实例变量,实例变量就是一个用 self 修饰的变量。
- 继承:即一个派生类(derived class)继承基类(base class)的字段和方法。继承也允许把一个派生类的对象作为一个基类对象对待。
- 实例化:创建一个类的实例,类的具体对象。
- 对象:通过类定义的数据结构实例。对象包括两个数据成员(类变量和实例变量)和方法。
三大基本特征:封装、继承、多态
面向对象的三个基本特征是:封装、继承、多态
1、封装
封装,也就是把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏。
封装的目的是增强安全性和简化编程,使用者不必了解具体的实现细节,而只是要通过外部接口,以特定的访问权限来使用类的成员。
面向对象的不就是使用程序处理事情时以对象为中心去分析吗,与面向过程不同,面向过程关心处理的逻辑、流程等问题,而不关心事件主体。而面向对象即面向主体,所以我们在解决问题时应该先进行对象的封装(对象是封装类的实例,比如张三是人,人是一个封装类,张三只是对象中的一个实例、一个对象)。比如我们日常生活中的小兔子、小绵羊都可以封装为一个类。
封装的优点:
- 将变化隔离
- 便于使用
- 提高复用性
- 提高安全性
2、继承
继承是面向对象的基本特征之一,当一个类继承自另一个类,它就被称为一个子类/派生类,继承自父类/基类/超类。它会继承/获取所有类成员(属性和方法)。继承机制允许创建分等级层次的类。继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例域和方法,或子类从父类继承方法,使得子类具有父类相同的行为。
继承的作用:
继承能让我们重新使用代码,也能更容易的创建和维护应用。
继承的分类:
- 单继承:一个类继承自单个基类
- 多继承:一个类继承自多个基类
- 多级继承:一个类继承自单个基类,后者则继承自另一个基类
- 分层继承:多个类继承自单个基类
- 混合继承:两种或多种类型继承的混合
继承概念的实现方式有三类:
实现继承、接口继承和可视继承。
Ø 实现继承是指使用基类的属性和方法而无需额外编码的能力;
Ø 接口继承是指仅使用属性和方法的名称、但是子类必须提供实现的能力;
Ø 可视继承是指子窗体(类)使用基窗体(类)的外观和实现代码的能力。
3、多态
多态性(polymorphisn)是允许你将父对象设置成为和一个或更多的他的子对象相等的技术,赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作。简单的说,就是一句话:允许将子类类型的指针赋值给父类类型的指针。“一个接口,多种实现”
实现多态的两种方式:
覆盖,是指子类重新定义父类的虚函数的做法。
重载,是指允许存在多个同名函数,而这些函数的参数表不同(或许参数个数不同,或许参数类型不同,或许两者都不同)。
多态的优点:
- 消除类型之间的耦合关系
- 可替换性
- 可扩充性
- 接口性
- 灵活性
- 简化性
五大基本原则
1、单一职责原则(SRP)
一个类应该有且只有一个去改变它的理由,这意味着一个类应该只有一项工作。
比如在职员类里,将工程师、销售人员、销售经理这些情况都放在职员类里考虑,其结果将会非常混乱,在这个假设下,职员类里的每个方法都要if else判断是哪种情况,从类结构上来说将会十分臃肿。
2、开放封闭原则(OCP)
对象或实体应该对扩展开放,对修改封闭。
更改封闭即是在我们对模块进行扩展时,勿需对源有程序代码和DLL进行修改或重新编译文件!这个原则对我们在设计类的时候很有帮助,坚持这个原则就必须尽量考虑接口封装,抽象机制和多态技术!
3、里氏替换原则(LSP)
在对象 x 为类型 T 时 q(x) 成立,那么当 S 是 T 的子类时,对象 y 为类型 S 时 q(y) 也应成立。(即对父类的调用同样适用于子类)
4、依赖倒置原则(DIP)
高层次的模块不应该依赖于低层次的模块,他们都应该依赖于抽象。具体实现应该依赖于抽象,而不是抽象依赖于实现。
5、接口隔离原则(ISP)
不应强迫客户端实现一个它用不上的接口,或是说客户端不应该被迫依赖它们不使用的方法,使用多个专门的接口比使用单个接口要好的多!
属性
1.类的公有属性
public_attrs:能在类的外部被使用或直接访问。在类内部的方法中使用时 public_attrs_attrs,在类的外部class_name.public_attrs。
2.类的私有属性
__private_attrs:两个下划线开头,声明该属性为私有,不能在类的外部被使用或直接访问。在类内部的方法中使用时 self.__private_attrs。
3.类的(公有)方法
在类的内部,使用 def 关键字来定义一个方法,与一般函数定义不同,类方法必须包含参数 self,且为第一个参数,self 代表的是类的实例。
self 的名字并不是规定死的,也可以使用 this,但是最好还是按照约定是用 self。
4.类的私有方法
__private_method:两个下划线开头,声明该方法为私有方法,只能在类的内部调用 ,不能在类的外部调用。self.__private_methods。
类实例化时魔法方法
1、何为魔法方法:
Python中,一定要区分开函数和方法的含义;
1.函数:类外部定义的,跟类没有直接关系的;形式: def func(*argv):
2.方法:class内部定义的函数(对象的方法也可以认为是属性);分为两种:
① python自动产生的(魔法方法):一般形式为 func(),python会在对应的时机自动调用该函数;
② 人为自定义的方法:一般和普通函数没有区别,只是定义在了class中而已
3.方法与函数的区别:
方法可认为是函数的特殊情况;
① 方法定义在class内部
② 方法的第一个参数应为 cls(类方法) 或者 self(实例方法)
2、魔法方法调用的顺序
class Student(object):
def __new__(cls, *args, **kwargs):
print('__new__')
return object.__new__(cls) # 必须返回父类的__new__方法,否则不不执行__init__方法,无法创建实例
def __init__(self,name):
print('__init__')
self.name = name
def __str__(self): # 作用:打印实例时显示指定字符串,而不是内存地址
print('__str__')
return self.name
def __call__(self, *args, **kwargs): # 当执行C()(*args) 或者 s1(*args) 就会执行__call__
print('__call__',*args)
def __del__(self): # 作用:清除无用的实例对内存的暂用
print('__del__')
#1、实例化时机会执行__new__、init
#2、执行 实例() 就会执行__call__ 方法,并将参数传递给__call__函数
#3、当打印实例时就会执行 str 方法下返回的字符串(默认返回的实例地址)
#4、析构方法:当删除实例时就会调用 del 方法
##魔法方法汇总:
1.基本方法
__int__(self) 定义当被 int() 调用时的行为
__float__(self) 定义当被 float() 调用时的行为
__round__(self[, n]) 当被round()调用时的行为 round(digit[, n]) 将digit数字保留n位精度 __hash__(self) 定义能被 hash() 调用的行为 __bytes__(self) 定义被 bytes() 调用的行为 __bool__(self) 定义被 bool() 调用的行为 返回True(1) 或 False(0)
__format__(self, form) 定义被 format()调用的行为
2.运算符方法
__add__(self, other) 加法:+
__sub__(self, other) 减法:-
__mul__(self,other) 乘法:*
__truediv(self, other) 除法:/ 注意是 truediv
__floordiv(self, other) 整数除法:// floor()即为向下取整的意思
__mod__(self, other) 求余:%
__pow__(self, other[, mod]) 乘方:** pow(x,y[,z]),若无Z,则为 return x**y若有Z,则为 return x**y%z
__divmod__(self, other) divmode() 返回值为元祖 (商值,余数)
__lshift__(self, other) 左移:<<
__rshift__(self, other) 右移:>>
__and__(self, other) 按位与:& 注意以下均为按位操作,非逻辑操作
__or__(self, other) 按位或:|
__xor__(self, other) 按位异或:^
3.反运算符方法
__radd__(self, other) 加法,如a+b,当a不支持__add__()操作时,调用此函数; 即在运算符的基础上加上 'r' 即可,以下雷同
__rsub__(self, other) other - self
4.增量赋值运算方法
__iadd__(self, other) 赋值加法:+= 即在赋值运算符之前加 'i' ,以下雷同
__isub__(self, other) 赋值减法:-= self = self - other
5.一元运算符方法
__pos__(self) 定义正号:+x
__neg__(self) 定义负号:-x
__abs__(self) 取绝对值
__invert__(self) 按位求反:~x
6.比较运算符方法
__gt__(self, other) 大于:>
__ge__(self, other) 大于等于:>=
__lt__(self, other) 小于:<
__le__(self, other) 小于等于:<=
__eq__(self, other) 相等:==
__ne__(self, other) 不等:!=
7.属性操作
__getattr__(self, name) 当用户访问一个不存在的属性时调用 注意 object/super() (所有类的基类) 是无该方法的
__getattribute(self, name) 访问存在的属性时调用 先调用此函数,如找不到该属性,再去调用上面的属性
__setattr__(self, name, value) 设置属性时调用
__delattr__(self, name) 删除一个属性时调用
__get__(self, instance, owner) 描述符被访问时调用 想详细了解,请点击这里
__set__(self, instance, value) 描述符被改变时调用
__delelte__(self, instance, value) 删除描述符时调用
8.容器类型操作
__len__(self) 求容器的大小(注意与capacity的区别) 可变和非尅便容器均具备 __len__ 和 __getitem__
__getitem__(self, key) 获取容器中指定元素的行为
__setitem__(self, key, value) 设置容器中指定元素的行为 只有可变容器拥有 __setitem__ 和 __delitem__
__delitem__(self, key) 删除容器中指定元素的行为
__iter__(self) 定义迭代器中元素的行为
__reversed__(self) 当调用reversed()函数时
__contains__(self, item) 成员运算符in/ not in的行为
PS: ①.以上所有的魔法方法,采用__xx__形式(__为双 “_”,双下划线)
②.以上魔法方法为Python解释器自动调用,当然也可以手动调用
③.魔法方法Python解释器自动给出默认的,因此除非需要改变其内部功能,其它时刻刻使用默认魔法方法
④.魔法方法是针对class而言的,脱离了”类“谈magic_method是没有意义的
⑤.*argv为可变的参数列表,类似C语言的va(variable argument),注意与指针的区别,python中暂时忘掉指针,因为python的内存机制都是解释器自动完成的
反射(hasattr和getattr和setattr和delattr)
反射的概念:
反射指的是通过 “字符串” 对 对象的属性进行操作。
- hasattr: 通过 “字符串” 判断对象的属性或方法是否存在。hasattr(ogj,name_str) 判断一个对象里是否有对应的字符串方法
class Dog(object):
def eat(self,food):
print("eat method!!!")
d = Dog()
#hasattr判断对象d是否有eat方法,有返回True,没有返回False
print(hasattr(d,'eat')) #True
print(hasattr(d,'cat')) #False
- getattr: 通过 “字符串” 获取对象的属性或方法。getattr(obj,name_str) 根据字符串去获取obj对象里的对应的方法的内存地址
class Dog(object):
def eat(self):
print("eat method!!!")
d = Dog()
if hasattr(d,'eat'): # hasattr判断实例是否有eat方法
func = getattr(d, 'eat') # getattr获取实例d的eat方法内存地址
func() # 执行实例d的eat方法
#运行结果: eat method!!!
- setattr: 通过 “字符串” 设置对象的属性或方法。使用stattr给类实例对象动态添加一个新的方法
def abc(self):
print("%s正在交谈"%self.name)
class Person(object):
def __init__(self,name):
self.name = name
p = Person("星珲)
setattr(p,"talk",abc) # 将abc函数添加到对象中p中,并命名为talk
p.talk(p) # 调用talk方法,因为这是额外添加的方法,需手动传入对象
# 打印结果 星珲正在交谈
setattr(p,"age",30) # 添加一个变量age,复制为30
print(p.age)
- delattr: 通过 “字符串” 删除对象的属性或方法。
class Person(object):
def __init__(self,name):
self.name = name
def talk(self):
print("%s正在交谈"%self.name)
p = Person("星珲)
delattr(p,"name") # 删除name变量
print(p.name)