面向对象(简写:OOP)
面向对象编程定义:利用类和对象来创建各种模型,来实现对真实世界的描述。
优点:使程序更容易理解和维护以及扩展代码。
类定义:用来描述具有相同的属性和方法的对象的集合。(简单讲就是个模板)
实例化定义:创建一个类的实例,类的具体对象。
对象定义:通过类定义的数据结构的实例
举例:一个最简单最小结构的类写法如下:
class class_name(object):
print('bababa')
举例一个正常类的写法:
class 类名称(object):
def __init__(self,name..) #构造函数
self.arg1 = name # 普通属性(成员变量),注:self就是实例对象本身,保存在对象内存里
def fun1(self): # 普通方法(动态属性)
print('...')
类进行实例化的运行原理:
1:将类中的对象和方法存储于内存中,
2:类进行实例化时,先在内存申请一块空间用于存放实例对象
3:把实例对象的内存地址和赋值参数传递给类模板
4:模板对传入的值进行self变量赋值,然后将self变量发送到实例对象内存空间。
类的三大特性:封装,继承,多态
-- 封装:类内部的数据和方法,对数据的赋值和内部调用对类外部而言是透明的。简单说就是隐藏实现的细节,使代码模块化。
特性:防止数据被随意修改,可以通过此对象对外接口进行直接访问。
另(类封装了属性和方法,对象封装了普通属性的值)
-- 继承:一个父类派生出的子类,在父类里定义的属性和方法自动被子类继承,实现一个角色的共同点和不同点的同时存在。使代码重用
注意:承可以多级继承,不过越多代码会越复杂,建议继承2-3级即可,继承的2个类之间应该是属于的关系。
-- 被继承的类命名:父类,基类,超类
-- 继承的类命名:子类,派生类
--种类:单继承和多继承。
--继承的过程:从一般到特殊的过程。
这里“一般”指:父类这样的对一个角色具有共同点。
这里“特殊”指:子类这样的对每一个角色的不同点的单独定义
--实现方式有2类:实现继承 和 接口继承
实现继承:指父类具有的功能,子类继承后直接调用不需再修改。
接口继承:指仅使用父类的属性和方法的名称,具体实现需要在子类里单独实现。
举例:单继承基本继承写法
1 class Person(object): 2 def __init__(self,name,age=22): 3 self.NAME = name 4 self.AGE = age 5 self.storage = 'Normal' 6 def talk(self): 7 print('custom ......') 8 class blackPerson(Person): 9 def __init__(self,name,age,job): 10 Person.__init__(self,name,age) #继承父类的属性(经典写法) 11 self.JOB = job 12 def talk(self): 13 Person.talk(self) # 继承父类的方法,不过这样没有意义, 14 print('非洲语言') 15 class writePerson(Person): 16 def __init__(self,name,mess): 17 Person.__init__(self,name) 18 self.MESS = mess 19 def talk(self): 20 print('speak ENGLISH')
-- 多态:同样的方法名的同时又对父类的方法做了不同的实现,这就是同一事务表现的多种形态。特点接口重用。
(举例:父类定义人会说话,子类分黄种人说汉语,白种人说英语)这就是多态。实现是改写父类的talk方法
举例:python中多态的写法
1 class Animal(object): 2 def __init(self,name): 3 self.name = name 4 def talk(self): 5 raise NotImplementedError('Subclass must implement abstrace method') #提示一个需要子类重写的错误 6 class Cat(Animal): 7 def talk(self): # 子类重写 8 return 'miao' 9 class dog(Animal): 10 def talk(self): # 子类重写 11 return 'wang' 12 d = dog() 13 c = Cat() 14 def animal_talk(arg): # 这里使用单独一个函数实现多态功能 15 return arg.talk() 16 print(animal_talk(d)) 17 print(animal_talk(c))
类的属性分如下:
-- 公有属性(静态字段):所有属于这个类的对象都可以访问的属性,(在类里直接定义的属性,class下一行定义的)
修改方法:1:通过对象修改,只改变该对象的属性值,相当于在对象本地内存新创建一个属性值(默认是引用类里的全局变量)
2:通过“类.公有属性名”修改,将改变的是全局属性值。
(疑问解释:类中的构造函数不都可以访问么,说的是实例对象,一个实例对象赋给的参数值在其他对象是无法访问的。所以构造函数不属于公有属性。
比如:实例化了2个对象a,b,其中实例化a的参数值(a=fun(1,2,3)),对象b就无法访问a的参数值。)
--成员属性(普通字段):构造函数中的self变量。
--私有属性:在类中以“__字符串”,双下划线开始的变量名即为私有属性。正常情况下私有属性在类外部是无法访问的。
访问方法有2种:1:通过在类里定义普通方法来return返回私有属性。2:强制访问写法:实例名._类名__私有属性名。
举例:使用第一个方法对外提供私有属性访问接口
def get_hart():
return self.__heart #这里就直接返回私有属性值
类的方法分如下:
-- 普通方法:类中定义的常用方法。def定义的
小知识(如何将普通方法变为私有方法,1:单独写一个函数,比如名为fun2,然后重写对象的方法名,如d1.fun = fun2,最后执行此对象的方法d1.fun(d1))
-- 析构方法:语法:def __del__(self),在引用的变量被清空的时候或者通过手工del删除引用的变量后就会自动调用此方法进行内存回收。
一般用于程序的收尾,比如服务器端要停止服务,这时就需要它来清空客户端的连接。(另说明,只要类被实例化了就算是使用中,将不会自动回收)
-- 类方法:只能访问类变量(公有属性),不能访问实例变量,一般用在实例化对象后,无法对本身限定好的数据进行修改。
举例说明:
class f1:
name = 'jack' #类变量
@classmethod #类方法
def fun(self):
print('classmethod %s' % self.name) # 这里数据只用使用name的值,无法修改
-- 静态方法:类里定义的方法,当不需要通过self往里传值的时候,就可以将此方法定义为静态方法。
举例说明:
class f1:
@staticmethod # 静态方法写法
def fun(): # 注意 静态方法是不需要self传值,这里不写self
print('staticmethod')
调用方法为: f1.fun()
-- 属性方法:把一个方法变成一个静态属性,属性就不需要加括号调用,一般用于最终展示给用户看得结果
举例说明:
class f1:
def __init__(self):
self.__food = None # 为属性方法设置的私有属性(用于传值的,如果不使用setter方法,就可以不写)
@property #属性方法 (静态属性)默认这里只能打印固定数据,无法赋值
def fun(self): # 方法名
print('property: %s' % self.name,self.__food)
@fun.setter # 实现对属性方法赋值功能(注意这里的fun要和上面的方法名一致)
def fun(self,food):
print('set to food:',food)
self.__food = food # 实现对私有属性的赋值
@fun.deleter # 实现删除属性方法的赋值
def fun(self):
del self.__food # 删除私有变量
调用方法:d = f1() ; d.fun # 打印属性方法
赋值方法:d.fun = 'baozi' # 赋值
删除赋值:del d.fun #删除
-- 内置方法:类里以:__名称__ 这样结构的方法称为类的内置方法。
1:__doc__:类的描述信息,就是打印类名下的注释信息
2:__module__:表示当前操作的对象在哪个模块
__class__:表示当前操作的对象的类名
3:__init__:构造函数,通过类创建对象时,自动触发执行。
4:__del__:析构函数,当对象在内存中被释放时,自动触发。
5:__call__:对象后面加括号,触发执行。
举例说明:
class cla1(object):
def __call__(self, *args, **kwargs):
print(args,kwargs)
ins = cla1()
ins('jace','vivi',name='jack',age=22) # 这里执行对象直接返回
返回:('jace', 'vivi') {'name': 'jack', 'age': 22}
6:__dict__:查看类或对象中的所有成员
print(类名.__dict__):打印类里的所有属性,不包括实例属性。print(实例名.__dict__):打印实例里所有的属性,不包括类属性
7:__str__:如果一个类中定义了此方法,那么在打印实例对象时,默认输出该方法的返回值
1 class Person(object): 2 def __init__(self, name, gender): 3 self.name = name 4 self.gender = gender 5 def __str__(self): 6 return '(Person: %s, %s)' % (self.name, self.gender) 7 现在,在交互式命令行下用 print 试试: 8 9 >>> p = Person('Bob', 'male') 10 >>> print p 11 (Person: Bob, male)
8:__repr__:在python3里发现直接运行实例和使用pring 输出的状态和__str__ 相同。没看出有什么区别。但是通过sqlalchemy的创建表结构里可以看出区别,
通过在表结构里定义__str__返回来的数据是内存对象,通过在表结构里定义__repr__返回来的是转换后的字符串。
9:__getitem__ / __setitem__ / __delitem__ :用于索引操作,分别表示获取,设置,删除数据。
举例说明:
1 class cla1(object): 2 def __init__(self): 3 self.data = {} 4 def __getitem__(self, key): 5 print("__getitem__: %s" % key) 6 return self.data.get(key) 7 def __setitem__(self, key, value): 8 print('__setattr__:%s,%s' % (key,value)) 9 self.data[key] = value 10 def __delitem__(self, key): 11 print('__delitem__:%s' % (key)) 12 ins = cla1() 13 ins['name'] = 'jack' #调用setitem赋值 14 print(ins['name']) # 调用getitem打印 15 print(ins.data) # 打印实例属性
10:__new__:先于构造函数执行,构造函数是通过New来自动执行的。所以new是用来创建实例的,默认不用写
用处:可以在类进行实例化之前,通过new进行定制
1 class foo(object): 2 def __init__(self,name): 3 self.name = name 4 print('foo --init--') 5 def __new__(cls, *args, **kwargs): 6 print('foo --new--') 7 return object.__new__(cls) # 继承父类的new方法,如果注销这个,将不会自动执行构造函数, 8 #以上这行解释,类foo也是一个对象,这里的cls就是foo对象本身,也就相当于self概念。 9 f = foo('jack')
11:__metaclass__ : 指定当前类的原类名称,可以修改原类对当前类进行重新设置。在上面的new执行之前,
执行顺序为:第一步执行metaclass的init方法,第二步执行metaclass的call方法,第三步执行foo函数的new方法,第四步执行foo的initd方法,
这就是实例化整个路线图。
看代码:
1 class Mytype(type): 2 def __init__(self): #第一步 3 super(Mytype.self).__init__(what,bases,dict) 4 def __call__(self,*args,**kwargs): #第二步 5 ..... 6 class foo(object): 7 __metaclass__ = 'Mytype' 8 def __init__(self,..): # 第四步 实例化完成 9 ..... 10 def __new__(cls,*args,**kwargs): 11 return object.__new__(cls) #第三步
看图:
类的 分类:有2种,第一种是:新式类。第二种是:经典类
-- 他们的区别:1:新式类需要在类名后加(object),经典类不需要,直接定义类名。2:继承方式写法不同。
-- 继承方式:在Python3中全部(新式类/经典类)为广度优先,在Python2中经典为深度优先,新式类为广度优先。
-- 继承写法:新式类使用super,经典类直接使用父类名初始化。
举例说明:
class A(object):
def __init__(self):
self.n = 'A'
class B(A):
def __init__(self):
self.n = 'B'
class C(A):
def __init__(self):
self.n = 'C'
class D(B,C):
pass
mess = fun4() ; print(mess.n)
解释:在python3中,无论是新式类还是经典类,继承顺序都是广度优先,以上顺序为:B->C-A
在python2中,新式类继承顺序为广度优先,如上,经典类是深度优先为:B->A->C
举例单独说明类中的self。(self == 实例对象)
class dog(object):
def __init__(self,name):
self.NAME = name
def sayhi(self):
print('wang wang wang , dog name %s' % (self.NAME))
dg1 = dog('jinba') # 相当于:dog(dg1,'jinba') 注意:self == dg1(实例对象)
dg1.sayhi() # 就相当于:dg1.sayhi(dg1),因为self在类中默认是传入的。