目录
2.1.3、成员方法(实例方法)和私有成员方法(示例在2.1.5)
3.1.1、__doc__、__module__、__class__、__add__
3.1.4、__getitem__、__setitem__、__delitem__
3.1.5、__getslice__、__setslice__、__delslice__
一、概述
- 面向过程:根据业务逻辑从上到下写垒代码
- 函数式:将某功能代码封装到函数中,日后便无需重复编写,仅调用函数即可
- 面向对象:对函数进行分类和封装,让开发“更快更好更强...”
二、面向对象编程的三大特性
面向对象的三大特性是指:封装、继承和多态。
2.1、封装
封装,顾名思义就是将内容封装到某个地方,以后再去调用被封装在某处的内容。
2.1.1、self形参
除了一些特殊的方法之外,在类中定义的方法第一个形式参数必须是self,这个self类似于java中的this关键字,代表了当前的对象,即已经实例化的对象。只不过,java中隐式地传入了这个参数,而python需要显式地定义这个参数。实例化对象时和调用类中方法时,传入的实参则不需要传当前对象,也就是说,使用时忽略self。
2.1.2、成员字段和私有成员字段(示例在2.1.5)
1、通常情况下,定义类的时候,会在构造方法中通过self参数来添加成员字段。这样做的话,这个类的每个实例化对象都会有这些字段,如果直接通过实例化的对象来添加字段的话,则只是对当前实例有效。一般情况下不会再其他的方法中添加字段。
2、私有成员字段,一般定义在类中,字段名以“__”开头,只属于这个类,外界需要访问的时候,则需要调用类提供的方法来访问。这类字段不可被继承。
2.1.3、成员方法(实例方法)和私有成员方法(示例在2.1.5)
1、定义一个成员方法和在外部定义函数一样都是使用def关键字,不同点就在于,除了一些特殊方法,类中的方法第一个参数都要是self。
2、私有成员方法,跟普通方法定义方式相同,不同点就是方法名以“__”开头,该类方法不可被继承。
2.1.4、类方法和静态方法(示例在2.1.5)
1、这两种方法形参不需要写self,可直接通过类名调用,因此,这两种方法看起来似乎和某个具体的示例没有关系,也因此不需要传入当前实例,也就是不需要self参数。当然,通过实例也可以调用这两种方法。
2、类方法:使用装饰器@classmethod。第一个参数必须是当前类对象,该参数名一般约定为“cls”,通过它来传递类的属性和方法(不能传实例的属性和方法)。
3、静态方法:使用装饰器@staticmethod。参数随意,但是方法体中不能使用类或实例的任何属性和方法;
2.1.5、属性
1、访问属性时可以制造出和访问字段完全相同的假象,属性由方法变种而来,如果Python中没有属性,方法完全可以代替其功能。属性内部进行一系列的逻辑计算,最终将计算结果返回。
2、定义时,①使用@property装饰器,并且只有self一个参数;②使用静态字段
3、调用时和访问成员字段方式相同,实例.属性名(方法名不加括号)
示例如下:
class Girl:
__age = None #私有成员字段
def __init__(self,name,age = 18):
self.name = name #self.name中的name是成员字段
self.__age = age
@classmethod#类方法
def clsmethod(cls):#当前类对象
print('class method')
@staticmethod#静态方法
def stamethod():
print('static method')
@property#通过装饰器来创建属性
def Age(self):#通过方法访问私有成员字段
return '姑娘芳龄:%d'%self.__age
def getAge(self):
return self.__age
GETAGE = property(getAge)
@property
def Name(self):
return self.name
@Name.setter
def Name(self,name):
self.name = name
@Name.deleter
def Name(self):
print('Name.delete')
def getName(self):
return self.name
def setName(self,name):
self.name = name
def delName(self):
print('delname')
NAME = property(getName,setName,delName,'description....')
Girl.clsmethod()
Girl.stamethod()
g = Girl('诸葛大力')
print(g.Age)
print(g.GETAGE)
print(g.Name)#调用@property
g.Name='小力'#@Name.setter
del g.Name#@Name.deleter
g.NAME = '大力'#setName(self,name)
print(g.NAME)#def getName(self):
del g.NAME#def delName(self):
'''
输出结果:
class method
static method
姑娘芳龄:18
18
诸葛大力
Name.delete
大力
delname
'''
2.2、继承
顾名思义,子类(派生类)继承父类(基类)的属性、成员字段和成员方法等。python支持多继承,因此会有方法冲突的问题,解决这个问题的机制简而言之,在方法没有被重写的情况下,无论何时何地都是从最左边的父类开始。不只是对成员方法是这样,对于构造方法等都是这样。分为两种情况。
记住一点,这里面所有的self都是指的子类的实例
2.2.1、被继承的多个父类没有共同的父类
意思就是被继承的多个父类,他们分别都没有继承共同的父类。在这种情况下,依据从左到右的搜索原则,首先从最左边的父类开始搜索欲调用的方法。示例如下:
class a:
def __init__(self):
pass
def abc(self):
pass
class b:
def __init__(self):
pass
def abc(self):
pass
def test(self):
abc(self)
class c(a,b):
def t(self):
test()
ins = c()
ins.t()
'''
这里只描述这种情况:
如例所示,实例c调用了test方法,在test方法没有被重写的情况下,去父类中找这个方法,首先是要去a类中找这个方法,但是a中没有这个方法,
然后再去b中找,找到了这个方法,test方法又调用了abc方法,显然,a、b中都包含这个方法,这个时候,回到最初的起点,在abc没有被重写的
情况下,去最左边的a类中找这个abc方法,a中存在此方法,故调用了a中的abc方法。
这里面所有的self都指的是ins这个实例,我觉得应该是这个原因,才使得这种搜索原则应运而生吧。
'''
2.2.2、被继承的多个父类有共同的父类
示例如下:
class p:
def abc(self):
pass
class a(p):
def __init__(self):
pass
class b(p):
def __init__(self):
pass
def test(self):
abc(self)
class c(a,b):
def t(self):
test()
ins = c()
ins.t()
'''
这里只说明这种情况:
如上代码所示,c调用了abc方法,依据从左到右的原则,在方法没有被重写的情况下,在父类中寻找test方法,从a-->b,在b中找到被执行,
再找abc方法,在abc方法没有被重写的情况下,在父类中寻找,先从a中开始找,a中不存在这个方法,这个时候不会搜索a与b共同的父类,也
就是说,如果a继承了另一个类,这个类继承了p,那么这个类也会被搜索,但是不会搜索p;然后再搜索b中的方法,依然没有找到,最后搜索
a,b共同的父类,也就是类p。
这里面所有的self也是指的ins这个实例。
'''
2.3、多态
python是一种弱类型语言,原生就是多态的,有利有弊。
三、类的创建
3.1、类中的特殊成员方法
3.1.1、__doc__、__module__、__class__、__add__
__doc__表示类的描述信息
__module__表示当前操作的对象在那个模块
__class__ 表示当前操作的对象的类是什么
__add__运算符重载+
3.1.2、__init__、__del__
__init__构造方法
__del__析构方法
3.1.3、__dict__、__str__
__dict__ 字典类型,类或对象中的所有成员
__str__ print实例化的对象的时候,默认调用
3.1.4、__getitem__、__setitem__、__delitem__
用于索引操作,如字典。以上分别表示获取、设置、删除数据
3.1.5、__getslice__、__setslice__、__delslice__
用于切片操作
3.1.6、__call__
对象后面加括号,触发执行。
注:构造方法的执行是由创建对象触发的,即:对象 = 类名() ;而对于 __call__ 方法的执行是由对象后加括号触发的,即:对象() 或者 类()()
3.2、类的创建过程
3.2.1、类本身也是对象
class Foo:#默认调用type类的构造方法来创建类
pass
obj = Foo()
print type(obj) # 输出:<class '__main__.Foo'> 表示,obj 对象由Foo类创建
print type(Foo) # 输出:<type 'type'> 表示,Foo类对象由 type 类创建
'''
通过type类创建Foo类
#type第一个参数:类名
#type第二个参数:当前类的基类
#type第三个参数:类的成员
'''
Foo = type('Foo',(object,), {'func': func})
所以,obj对象是Foo类的一个实例,Foo类对象是 type 类的一个实例,即:Foo类对象 是通过type类的构造方法创建。
3.2.2、__metaclass__
类中有一个属性 __metaclass__,其用来表示该类由谁来实例化创建,所以,我们可以为 __metaclass__ 设置一个type类的派生类,从而查看类创建的过程。示例如3.2.3
3.2.3、过程
class MyType(type):
def __init__(self,what,bases=None,dic=None):
super(MyType,self).__init__(what,bases,dic)#执行type的构造方法
print(self,'this is mytype init')
def __call__(self, *args, **kwargs):
print(self,"this is Mytype call")
return type.__call__(self, *args, **kwargs)
class Foo(object,metaclass=MyType):#指定使用MyType来创建类
def __new__(self, *args, **kwargs):
print(self,'this is Foo new')
return object.__new__(self, *args, **kwargs)
def __init__(self):
print(self,'this is Foo init')
def func(self):
print('hello foo')
f = Foo()
①通过class Foo()来调用MyType类的构造函数,从而实例化MyType类,而这个实例就是Foo
②f = Foo(),从而会调用MyType中的__call__()方法,上面这个实例中调用type类中的__call__(),在type类中,有一条语句是:self.__new__(self,*args,**kwargs),从而调用了Foo类中的__new__()方法,执行完__new__()之后最后调用Foo中的构造方法,最终创建了Foo的实例f.
大致过程如下盗的一张图:
四、反射
反射是将字符串映射到示例的变量或者示例的方法。通俗点将就是可以用字符串来使用实例和实例中方法的技术。想象场景:在浏览器的地址栏中输入对应的网址,对应的网站也就会有反应,并给你返回对应的网页。我们输入到浏览器地址栏的url
是一个字符串,这个字符串的url
到web服务器上后是怎么找到对应的代码函数并执行后给我们返回内容的。
4.1、反射中的四个方法
- getattr 获取指定字符串名称的对象属性
- setattr 为对象设置一个对象
- hasattr 判断对象是否有对应的对象(字符串)
- delattr 删除指定属性
这里的属性指的是类中的字段和方法,但是不能获取私有字段和方法。
4.2、示例一:基本用法
class Foo:
stat = 'on'
def __init__(self,name,age):
self.name = name
self.age = age
def show(self):
return '%s-%s'%(self.name,self.age)
obj = Foo('alex',18)
'''以下三种方式都可以取到name这个成员字段的值'''
b = eval('obj.name')
print(b)
name = obj.__dict__['name']
print(name)
'''使用字符串获取变量'''
print(getattr(obj, 'name'))
'''获取并执行成员方法show'''
func = getattr(obj, 'show')
print(func)
print(func())
'''对成员的其他操作'''
print(hasattr(obj, 'name'))#变量是否存在
setattr(obj, 'key', 'value')#设置变量
print(getattr(obj, 'key'))
delattr(obj, 'key')#删除变量
'''对引入的模块内容操作'''
import day13.new5_test as new5
print(getattr(new5, 'name'))
func = getattr(new5, 'func')
print(func)
func()
clas = getattr(new5, 'cla')
print(clas)
print(clas().show())
4.3、示例二:动态加载和执行模块或方法
temp = "re"
model = __import__(temp)
if __name__ == '__main__':
txt = "hj123uo"
pattern = model.compile(r"\d+")#匹配连续的数字
print(model.search(pattern, txt).group())
'''
输出结果:
123
'''
temp = "re" # 要引入的模块
func = "compile" # 要使用的方法
model = __import__(temp) # 导入模块
function = getattr(model, func) # 找到模块中的属性
if __name__ == '__main__':
txt = "hj123uo"
pattern = function(r"\d+") # 这里执行funcation()就等于执行re.compile()函数
print(model.search(pattern, txt).group())
'''
输出结果:
123
'''
五、单例模式(Singleton Pattern)
该模式的主要目的是确保某一个类只有一个实例存在。也就是说在整个系统中,如果你希望某个类只能出现一个实例。