一丶面向对象技术简介
- 类(Class): 用来描述具有相同的属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法。对象是类的实例。
- 方法:类中定义的函数。
- 类变量:类变量在整个实例化的对象中是公用的。类变量定义在类中且在函数体之外。类变量通常不作为实例变量使用。
- 数据成员:类变量或者实例变量用于处理类及其实例对象的相关的数据。
- 方法重写:如果从父类继承的方法不能满足子类的需求,可以对其进行改写,这个过程叫方法的覆盖(override),也称为方法的重写。
- 局部变量:定义在方法中的变量,只作用于当前实例的类。
- 实例变量:在类的声明中,属性是用变量来表示的。这种变量就称为实例变量,是在类声明的内部但是在类的其他成员方法之外声明的。
- 继承:即一个派生类(derived class)继承基类(base class)的字段和方法。继承也允许把一个派生类的对象作为一个基类对象对待。例如,有这样一个设计:一个Dog类型的对象派生自Animal类,这是模拟"是一个(is-a)"关系(例图,Dog是一个Animal)。
- 实例化:创建一个类的实例,类的具体对象。
- 对象:通过类定义的数据结构实例。对象包括两个数据成员(类变量和实例变量)和方法。
和其它编程语言相比,python 在尽可能不增加新的语法和语义的情况下加入了类机制。
Python中的类提供了面向对象编程的所有基本功能:类的继承机制允许多个基类,派生类可以覆盖基类中的任何方法,方法中可以调用基类中的同名方法。
对象可以包含任意数量和类型的数据。
1.类中带下划线开头的变量特点
类中的方法,其实就是类中的函数,可以分为:实例方法,类方法,静态方法。方法和字段一样,也是属于类的属性,所以也具有运行中修改的特效, 但一般不推荐这样做。
我在类的基本语法中,介绍了构造器方法:__init__ 、__new__;解构器方法:__del__;
注意,这里虽然是以两个下划线(__)开头,但同时以两个下划线(__)结尾,这里表明其是一个‘魔法方法’,关于类中的魔法方法,将起一篇进行说明。
但是,如果单纯只以两个下划线开始,则依然是私有化的意思,看代码示例:
class Test(object):
def __scolia__(self): # 一个类似魔术方法,并不是私有化
return 'scolia'
def __Good(self): # 私有方法
return 'good'
a = Test()
print a.__scolia__() # 魔法方法可以在直接访问
print a.__good() # 私有方法不能直接访问
同样的,和字段私有化一样,我们也可能同特殊手段进行强制访问:
print a._Test__good() # 强制访问
当然,私有方法也可以在类的内部访问,和私有字段一样。
所以说,属性的私有化都是对访问入口进行混淆,同样的,也不建议强制访问私有属性。
也许这里的‘魔法方法’看起来并不‘魔法’,详情将以后解释。
实例方法:
在 __init__ 构造器中,提起过其是一个实例方法,实例方法的特点就是:
1.方法的第一个参数必须是 self,当然这是约定俗成的写法,你可以将 self 换成 abc 之类的,但是为了别的程序员能看得懂,还是统一用 self 吧。这里的 self 代表实例本身,也就是说如果我实例化时使用的是: a = Test() ,那么 self 就代表 a 这个实例,我们可以在很多构造器中看到类似 self.scolia = 'good' 的写法,其实这个写法和在类外面 a.scolia = 'good' 效果一样,是为了添加属性,只不过 __init__ 方法是实例化时自动调用的函数,所以适合进行初始属性的创建。
2.实例方法在调用的时候,self 是自动传递的,所以不需要我们再处理。
3.实例方法一般要有实例才能调用,当然也有特殊的调用方法。
代码示例:
class Test(object):
def __init__(self, a, b): # 构造器在实例创建时进行属性的初始化
self.a = int(a)
self.b = int(b)
def abc(self, c): # 实例方法
print self.a + self.b + int(c) # 因为self是自动传递的,所以我们可以在实例方法中调用实例的属性
a = Test(123, 321) # 我们只要为 a 和 b 传参就行了
a.abc(666) # 同样的,只要为 c 传参
这里,将引入一个绑定 (binding) 的概念,其主要和方法的调用有关。
首先,我们知道方法是类的属性,而不是实例的属性,在上篇博文类的属性和实例的属性中我们也讨论过这个问题。
其次,方法只有在其所属的类拥有实例时,才能被调用。当一个类存在实例后,方法才被认为是绑定到这个实例。没有实例的时候,方法是未绑定的。
最后,任何一个方法定义的第一个参数都是变量 self ,它表示调用此方法的实例对象。
很明显这里的绑定针对的是实例方法。因为如果没有实例的话,self 就无法传递,这将导致参数的不足,当然就无法调用了。
但是,我们可以自己传递 self 来调用未绑定的方法。调用未绑定的方法通常是在我们继承了一个父类后, 我们覆盖了父类中的某个方法,但是为了实现代码重用,我们又想在子类中调用父类的方法。单纯的复制父类中的代码明显不是一个好选择, 除了浪费系统资源之外,还有可能在复制的时候出错,而且以后修改父类的代码之后,还要修改相应子类中的代码,实在太低效,这个时候就是调用未绑定方法的场景。
代码示例:
class abc(object):
def __init__(self, a):
self.a = -int(a)
class Test(abc):
def __init__(self, a, b):
abc.__init__(self, a) # 调用父类的构造器,并手动传递 self
self.b = b
def fangfa(self):
print self.a + self.b # 属性 a 由父类的构造器创建,b 由子类构造器创建
a = Test(123, 321) # 我们只创建了子类的实例,而没有创建父类的实例
a.fangfa()
本来我们没有创建父类的示例,是无法调用父类的实例方法的,但是我们手动传递了实例方法需要的 self 参数,就可以实现调用了。
这里的顺序是,我们创建了 Test 的实例,其 self 是自动传递的,故 Test 的构造方法 __init__(self, a, b) 中 self 就代表实例 a,而我们又调用了父类的 abc.__init__(self, a) 这里的 self 就是子类的实例 a ,参数 a 就是我们传的 123,而父类中 self.a = -int(a) ;最后我们可在子类的方法中调用 self.a 这个属性。
2.Python面向对象的三大特性
一、继承
面向对象中的继承就是继承的类直接拥有被继承类的属性而不需要在自己的类体中重新再写一遍,其中被继承的类叫做父类、基类,继承的类叫做派生类、子类。在python3中如果不指定继承哪个类,默认就会继承Object类,而继承了Object类的类就叫做新式类,而在python2中如果不指定继承哪个类也不会默认去继承Object类,而没有继承Object类的类就叫做经典类。经典类和新式类的不同就在于对方法的搜索顺序不同,经典类是深度优先即先找自己类内,如果没有就找左边第一个父类,没找到继续从这个父类的父类中找依次类推直到找到最上一级的父类也没找到再找左边第二个父类,然后再重复之前的过程,直到所有父类找一遍没找到就报错;而新式类是广度优先,当下一个类可以通过其他类找到时就先不去找它,而是找继承关系中与它的子类同级的其他类,依次类推直到最后找到object类没有找到指定方法就报错。新式类搜索顺序图示如下,还可以通过类名.mro()查看新式类继承中的属性搜索顺序
二、单继承与多继承
在其他语言中只支持单继承即class 类名(父类名),而python支持多继承,用逗号将多个父类隔开即class 类名(父类名1,父类名2,。。。。)
三、继承与抽象
抽象就是把一类事物的共有特性提取出来,继承则是把父类的属性拿过来并且还拥有自己的属性。抽象是包含的范围越来越大,共性越来越少,继承则是包含的返回越来越小,共性越来越多。我们定义父类的过程就是抽象,定义子类的过程就是继承。
四、父类方法重写
我们把子类有而父类没有的方法叫做子类的派生方法,而父类有子类也有的方法叫做对父类方法的重写,因为按照类方法的搜索顺序一个方法如果在子类中有就不会再从父类中找了,结果就是父类中的方法无法调用了,如果既想执行父类中的方法同时在子类中又能定义新功能,就需要先把父类中的这个方法单独继承过来,在python中只能使用父类名.方法名(self,父类的其他参数)的方式,在python3中可以使用super函数来实现,比如super().父类方法名(除self外的其他参数),其实在super函数中还需要传入子类名和子类对象(在类中用self),但是我们使用时不需要特意去传,除非在类外单独调用父类的方法。注意在继承父类方法时父类的参数除了需要在父类的方法中传递还需要在子类重写的方法中传递
class Animal:
def __init__(self,name,life_value,aggr):
self.name=name
self.life_value=life_value
self.aggr=aggr
def eat(self):
self.life_value+=10
class Person(Animal):
def __init__(self,money,name,life_value,aggr):
super().__init__(name,life_value,aggr)
self.money=money
def attack(self,obj):
obj.life_value-=self.aggr
五、接口类
接口类是用于规范子类的方法名定义用的,继承接口类的子类可以不存在任何逻辑上的关系但是都需要实现某些共同的方法,为了让这些子类的方法名能够统一以便之后调用这些方法时不需要关注具体的对象就用接口类规范了这些方法的名字,子类一旦继承了接口类就必须实现接口类中定义的方法,否则在子类实例化的时候就会报错,而接口类本身则不需要实现去实现这些方法。
1 from abc import ABCMeta,abstractmethod
2 class Payment(metaclass=ABCMeta):
3 @abstractmethod
4 def pay(self,money):pass
5
6 class Wechatpay(Payment):
7 def pay(self,money): #子类中必须定义接口类中有的方法,否则实例化会报错
8 pass
9
10 w1=Wechatpay()
六、抽象类
抽象类的作用和接口类一样,只是继承它的子类一般存在一些逻辑上的关系,且抽象类中的方法可以去实现,子类在重写时用super函数调用抽象类的方法即可,同时在用抽象类时使用单继承,使用接口类时使用多继承
七、多态
多态就是不同的对象可以调用相同的方法然后得到不同的结果,有点类似接口类的感觉,在python中处处体现着多态,比如不管你是列表还是字符串还是数字都可以使用+和*。
八、封装
封装就是把类中的属性和方法定义为私有的,方法就是在属性名或方法名前加双下划线,而一旦这样定义了属性或方法名后,python会自动将其转换为_类名__属性名(方法名)的格式,在类的内部调用还是用双下划线加属性名或方法名,在类的外部调用就要用_类名__属性名(方法名)。父类的私有属性和方法,子类无法对其进行修改。
九、类的装饰器
property属性装饰器:将类内的方法的调用方式和属性一样,这个装饰器还有和其配套的setter、deleter。
class Demo:
@property
def p(self):
print('property func')
@p.setter
def p(self,num):
print('property_setter')
@p.deleter
def p(self):
print('在删除')
d=Demo()
d.p
d.p=10
del d.p
--------------------------------------------------------------------------------------
property func
property_setter
在删除
staticmethod静态方法装饰器:将类内的方法变成普通的函数,或者把类外的函数放到类内当作方法调用
class A:
@staticmethod
def sum(): #这个方法跟普通函数没有区别
print('staticmethod')
A.sum() #用类名调用
--------------------------------------------------------------------------------------
staticmethod
claSSMethod类方法装饰器:该方法用于操作类属性,无法操作对象属性
class A:
role='male'
@classmethod
def sum(cls): #用于操作类属性
print(cls.role)
A.sum() #用类名调用
--------------------------------------------------------------------------------------
male
十、isinstance和type的区别以及issubclass
isinstance和type都可以用于判断对象和指定类间的关系,但是isinstance的判断没有type准确,它无法正确判断子类的对象和其父类的关系
class A:
pass
class B(A):
pass
b=B()
print(isinstance(b,B))
print(isinstance(b,A))
print(type(b) is B)
print(type(b) is A)
--------------------------------------------------------------------------------------
True
True
True
False
issubclass用于判断给定的两个类,前者是否是后者的子类
十一、反射
hasattr(对象或类名,‘属性或方法名’) 判断指定的对象或类中是否存在指定的属性或方法,有返回True
getattr(对象或类名,'属性或方法名') 获取对象或类的指定属性值或方法的内存地址
setattr(对象或类名,‘新属性名’,新属性值) 给对象或类添加新的属性或方法
delattr(对象或类名,‘新属性名’) 删除之前添加的属性
十二、类的内置方法
__doc__ :输出类的描述信息
__module__ :表示当前操作的对象在那个模块
__class__ : 表示当前操作的对象的类是什么
__dict__ :查看类或对象中的所有成员 类调用打印类的所有属性,不包括实例属性。实例调用打印所有实例属性
__str__ 格式化输出%s输出该方法的值
__repr__ 格式化输出%r输出该方法的值,并且%s在没有__str__方法时也是输出该方法的值
__del__ del 执行该方法
__getitem__ 用对象加[]方式取值
class A:
def __init__(self):
self.names=['egon','alex','eva'] #可以是其他序列
def __getitem__(self, item):
print(self.names[item])
a=A()
a[1]
----------------------------------------------------------
alex
__setitem__ 添加值
__delitem__ 删除值
__new__ 用于创建没有属性的对象,调用object的__new__即可不需要自己实现。可以利用该方法实现单例模式
__call__ 对象加括号执行该方法
__len__ len()执行该方法
__eq__ ==运算输出该方法的值
__hash__ hash执行该方法
二丶代码实现(三维向量类)
1 class Vecter3:
2 def __init__(self, x=0, y=0, z=0):
3 self.X = x
4 self.Y = y
5 self.Z = z
6 def __add__(self, n):
7 r = Vecter3()
8 r.X = self.X + n.X
9 r.Y = self.Y + n.Y
10 r.Z = self.Z + n.Z
11 return r
12 def __sub__(self, n):
13 r = Vecter3()
14 r.X = self.X - n.X
15 r.Y = self.Y - n.Y
16 r.Z = self.Z - n.Z
17 return r
18 def __mul__(self, n):
19 r = Vecter3()
20 r.X = self.X * n
21 r.Y = self.Y * n
22 r.Z = self.Z * n
23 return r
24 def __truediv__(self, n):
25 r = Vecter3()
26 r.X = self.X / n
27 r.Y = self.Y / n
28 r.Z = self.Z / n
29 return r
30 def __floordiv__(self, n):
31 r = Vecter3()
32 r.X = self.X // n
33 r.Y = self.Y // n
34 r.Z = self.Z // n
35 return r
36 def show(self):
37 print((self.X,self.Y,self.Z))
38
39 v1 = Vecter3(1,2,3)
40
41 v2 = Vecter3(4,5,6)
42
43 v3 = v1+v2
44
45 v3.show()
46
47 v4 = v1-v2
48
49 v4.show()
50
51 v5 = v1*3
52
53 v5.show()
54
55 v6 = v1/2
56
57 v6.show()
此类可实现三维向量的加减乘除
运行后为
三丶代码实现(程序类)
这个程序运用类与正则表达式匹配来写,可以删除重复的英文并输出,这里输入 This is my name
import re
class Good:
def __init__(self,n):
self.n = n
def love(self):
s1 = re.split(r' ',self.n)
s2 = sorted(set(s1),key=s1.index)
print(s2)
b = Good('This is is my name')#此处可自行输入字符串
b.love()
接下来我们看运行结果