面向对象
面向对象,简单来说用类对一切对象进行描述的一种编程思维和方法.在这里就不多做介绍(因为我也只是意会到一点点).根据面向对象的概念,我们需要注意的几个概念:
类(Class): 用来描述具有相同的属性和方法的对象的集合.它定义了该集合中每个对象所共有的属性和方法.
对象(Object):通过类定义的数据结构实例.对象包括两个数据成员(类变量和实例变量)和方法。
类变量:类变量在整个实例化的对象中是公用的,类变量定义在类中且在函数体之外,类变量通常不作为实例变量使用.
实例变量:定义在方法中的变量,只作用于当前实例.
数据成员:类变量或者实例变量用于处理类及其实例对象的相关的数据。
方法:类中定义的函数.
继承:即一个派生类(derived class)继承基类(base class)的字段和方法.也称为父类和子类
方法重写:如果从父类继承的方法不能满足子类的需求,可以对其进行改写.
实例化:创建一个类的实例,类到具体对象的过程
属性: 不管是变量或者是方法,都称为属性.变量一般称为静态属性,方法称为动态属性
面向对象的三大特性
1, 封装: 把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏.
2, 继承: 让不同类型的对象获得其它类型对象的属性和方法.
3, 多态: 实现接口重用, 即可以不同类型对象使用相同的方法,也可以一个对象的相同方法在不同情形有不同的表现形式
类
类的封装,基本构成和使用
1, 创建类,先来看例子
class Test(object):
'''class_doc'''
class_var = 'class_var'
def class_func(self):
obj_var = 'obj_var'
print(obj_var)
class | 定类的关键字通常类的首字母使用大写
object | 新式类的所有类的基类,经典类和新式类在后面阐述,python3中建议使用这种形式定义类
class_doc | 类的文档,可以通过__doc__方法查看
obj_var | 实例变量,需要对类进行实例化生成对象后,才能通过对象是属于具体实例的.静态属性
def | 定义类方法的关键字,和普通函数作用相同.动态属性
self | 类方法必须有的参数,代表的是实例自身
class_var | 类变量,可以在外部直接调用,不需要先进行实例话
2, 实例化,在python中使用类似函数调用的方式实例化类,并没有其它语言中的特殊关键字
t1 = Test()
#t1 就是类Test的具体对象
3, 访问属性,同样是上例子
class Test(object):
class_var = 'class_var'
def obj_func(self):
obj_var = 'obj_var'
print(obj_var)
t1 = Test()
Test.class_var = 'modify from Test'
print(Test.class_var)
#print(Test.obj_func())
t1.class_var = 'modify from t1'
print('Test:',Test.class_var)
print(t1.class_var)
print(t1.obj_func())
#print(t1.obj_var)
我们可以发现几个情况:
- (1)类变量不需要实例化可以访问,实例在实例化后会共用类变量,
- (2)不能通过实例修改类变量,修改的自身的那份类变量.相当于新生成了一个实例变量.
- (3)类里定义的方法通常没有实例化,是不能访问.
- (4)类方法里定义的实例变量是不能在外部直接调用的.
4, 类的内置方法,可以查看一些类的基本信息.(有些属性实例不能使用.)
__init__ | 构造方法
__del__ | 析构函数
__name__ | 查看类名
__dict__ | 查看类属性成员
__doc__ | 查看类文档
__class__ | 查看对象所属的类
__module__ | 查看对象所在模块
__bases__ | 查看基类
__mro__ | 查看继承顺序
__call__ | 让实例化的对象可调用(即可以加执行符号())
__str__ | 打印对象是,返回这个方法的值,通常用在实例化的对象定制信息
__getitem__ | 见第9小点
__setitem__ | 见第9小点
__delitem__ | 见第9小点
__new__ | 实例化方法
__slots__ | 限制类属性
__metaclass__ | 查看元类
5, 构造函数,类的实例化时执行的函数,通常用来实现对象的初始化._init__
class Test():
name = 'class_var'
def __init__(self,name,choice=None):
self.name = name
self.choice = choice
if self.choice == 'yes':
self.say_hello()
else:
self.other()
def say_hello(self):
print('Welcome,%s'%self.name)
def other(self):
print('No choice')
t1 = Test('sylar','yes')
print(t1.name)
#Test.name
t2 = Test('tom')
通过这个例子可以发现,构造函数中定义的属性是针对实例本身,并且也印证了类中的关键字self是指对象而不是类
6, 析构函数,__del__()也是可选的,如果不提供,则Python 会在后台提供默认析构函数.在对象销毁时自动执行的函数,显示的执行可以使用del obj
import time
class del_test(object):
def say_bye(self):
print('see you next time')
def __del__(self):
self.say_bye()
o1 = del_test()
del o1
time.sleep(3)
o2 = del_test()
7, 私有属性,无法在外部调用,但是可以在内部调用,从而隐藏某些属性或方法.
class Test(object):
def __init__(self,name,id_status):
self.name = name
self.__status = id_status
def show_status(self):
print(self.__status)
t1 = Test('sylar','some info')
t1.show_status()
t1.__status
单下划线、双下划线、头尾双下划线说明:
__foo__: 定义的是特殊方法,一般是系统定义名字,类似 init() 之类的.
_foo: 以单下划线开头的表示的是 protected 类型的变量,即保护类型只能允许其本身与子类进行访问,不能用于 from module import *
__foo: 双下划线的表示的是私有类型(private)的变量, 只能是允许这个类本身进行访问了.
8, __call__方法,让不可执行的对象变的可执行
class Animal(object):
def __call__(self, *args, **kwargs):
print('call func')
t = Animal()
t()
Animal()()
9, __getitem__,__setitem__,__delitem__.用类模拟类似字典的操作
class Test(object):
def __getitem__(self,key):
print('__getitem__:',key)
def __setitem__(self, key, value):
print('__setitem__:',key,value)
def __delitem__(self, key):
print('__delitem__:',key)
t1 = Test()
t1['name']
t1['name'] = 'sylar'
del t1['name']
类的继承
1, 继承,代码重用,派生类(子类)会完全继承基类(父类)的所有属性.
class Base_Class(object):
def __init__(self,name):
self.name = name
self.say_hi()
def say_hi(self):
print('Welcome, %s!'%self.name)
def call_me(self):
print("%s"%self.name)
class Sub_Class(Base_Class):
pass
t1 = Sub_Class('sylar')
t1.call_me()
2, 方法重写: 当子类中定义的属性或方法和父类重名时,子类会覆盖父类中的定义.有些时候需要父类的方法,又需要做一些改动.可以直接在子类中调用父类的方法然后再添加新的代码,或者使用super函数.(建议使用super)
class Base_Class(object):
def __init__(self,name):
self.name = name
def call_me(self):
print("%s"%self.name)
class Sub_Class(Base_Class):
def call_me(self):
#super(Sub_Class,self).call_me()
Base_Class.call_me(self)
print('modify for Sub_Class')
t1 = Sub_Class('sylar')
t1.call_me()
3, 多继承顺序,又分为经典类和新式类,在python3中默认都是使用广度优先. 使用__mro__方法可以查看继承的查找顺序
class Base_A:
def __init__(self):
print('from A')
class Base_B(Base_A):
pass
class Base_C(Base_A):
def __init__(self):
print('from C')
class Sub_Class(Base_B,Base_C):
pass
t1 = Sub_Class()
print(Sub_Class.__mro__)
这段经典类的代码在python2X 输出为'from A'深度优先,在python3x输出为'from C'广度优先
python2 的新式类使用的广度优先
类的多态
接口重用,使用统一的接口对子类的方法进行调用
class Animal(object):
@staticmethod
def run_func(obj):
obj.run()
class Cat(Animal):
def run(self):
print('Cat is running')
class Mouse(Animal):
def run(self):
print('Mouse is running')
c1 = Cat()
m1 = Mouse()
Animal.run_func(c1)
Animal.run_func(m1)
类内置装饰器
1, 静态方法 staticmethod不能访问类和实例属性,相当于和类本身没有什么关系,只是需要通过类名来调用这个方法.而且实例化后self也不会被传入到这个方法中.前面的多态就是一种使用场景.
2, 类方法 classmethod只能访问类变量不能访问实例变量
class Animal(object):
name = 'Tom'
def __init__(self):
self.name = 'Jerry'
@classmethod
def run_func(self):
print('%s is running'%self.name)
t = Animal()
t.run_func()
3, 属性方法 把一个方法当作静态属性来使用
单独使用,不能传参数
class Animal(object):
def __init__(self):
self.name = 'Jerry'
@property
def run_func(self):
print('%s is running'%self.name)
t = Animal()
t.run_func
配合setter接受赋值,实现类似传入参数的功能
class Animal(object):
def __init__(self):
self.__who = None
@property
def run_func(self):
print('%s is running'%self.__who)
@run_func.setter
def run_func(self,who):
#print('set to name',who)
self.__who = who
配合deleter删除,默认property装饰器转换过来的属性是不能删除的.如果要删除就要使用deleter
class Animal(object):
def __init__(self):
self.__who = None
@property
def run_func(self):
print('%s is running'%self.__who)
@run_func.setter
def run_func(self,who):
#print('set to name',who)
self.__who = who
@run_func.deleter
def run_func(self):
del self.__who
t = Animal()
t.run_func = 'Tom'
t.run_func
del t.run_func
类的定制(元类)和type
由于python是解释型语言,所以函数和类的定义不是编译时定义的,而是运行时动态创建的.在python中所有的类都是继承object,并由type方法生成的.type接收三个参数:类名,继承的父类元组,需要绑定的函数. 就可以生成一个类
def my_init(self,name):
self.name = name
def say_hi(self):
print('Hello %s'%self.name)
Test = type('Test', (object,), {'say_hi' : say_hi, '__init__' : my_init,})
t = Test('sylar')
t.say_hi()
1, __new__类的实例化方法,在所有类实例化的时候都是通过new方法
class MyClass(object):
def __init__(self):
print('some code in init!')
def __new__(cls, *args, **kwargs):
print('some code in new!')
return object.__new__(cls)
t1 = MyClass()
- new是执行在init之前的,并且通过return触发init的执行
- return object.__new__(cls)相当于super去继承但这时候还没生成self,
- cls相当于self,指代MyClass这个类的自身
2,metaclass元类,创建类的类,类是metaclass创建的实例.在python中自定义类需要先创建metaclass再创建类.python3和python2的语法有所改变
python3
def my_add(self,value):
self.append(value)
class ListMetaclass(type):
def __new__(cls, name, bases, attrs):
attrs['my_add'] = my_add #这里也可以使用lambda
return type.__new__(cls, name, bases, attrs)
class MyList(list,metaclass=ListMetaclass):
pass
l1= MyList()
l1.my_add(1)
l1.my_add(2)
print(l1)
python2
class ListMetaclass(type):
def __new__(cls, name, bases, attrs):
attrs['add'] = lambda self, value: self.append(value)
return type.__new__(cls, name, bases, attrs)
class MyList(list):
__metaclass__ = ListMetaclass
属性锁定
前面已经说了python类的属性可以动态创建的,为了达到限制的目的,Python允许在定义class的时候,定义一个特殊的__slots__变量,来限制该class实例能添加的属性
class Test():
#__slots__ = ('name','age')
def __init__(self,name,age):
self.name = name
self.age = age
t = Test('sylar',18)
t.job = 'it'
反射
判断类或实例是否有相应的属性,简单来说就是通过字符串来操作类.
hasattr(obj,name_str),判断对象里是否有对应字符串的属性
class Test():
#__slots__ = ('name','age')
def __init__(self,name,age):
self.name = name
self.age = age
t = Test('sylar',18)
choice = input()
res = hasattr(t,choice)
print(res)
getattr(obj,name_str),根据字符串获取对象相应的属性或是属性的内存地址
class Test():
#__slots__ = ('name','age')
def __init__(self,name,age):
self.name = name
self.age = age
t = Test('sylar',18)
choice = input()
res = getattr(t,choice)
print(res)
setattr(obj,name_str,attrs),根据字符串给对象设置相应的属性
class Test():
#__slots__ = ('name','age')
def __init__(self,name,age):
self.name = name
self.age = age
t = Test('sylar',18)
choice = input()
setattr(t,choice,'Tom')
v = getattr(t,choice)
print(t.name,v)
delattr(obj,name_str),删除字符串对应的属性
class Test():
#__slots__ = ('name','age')
def __init__(self,name,age):
self.name = name
self.age = age
t = Test('sylar',18)
choice = input()
delattr(t,choice)
print(t.name)