python修炼之路---面向对象
面向对象编程
面向对象编程:使用类和对象实现一类功能。
类与对象
类:类就是一个模板,模板里可以包含多个函数,函数里实现一些功能。
对象:是根据模板创建的实例,通过实例对象可以执行类中的函数。
面向对象应用
根据模板创建一系列例子的时候;当多个函数需要用到共同的参数时(提取公共参数放入构造函数中);应用场景(SSH)
定义类
class 类: def func1(self): def func22(self):
类的特性
封装:将内容封装到某个地方,以后再去调用被封装在某处的内容;对于面向对象的封装来说,其实就是使用构造方法将内容封装到对象中,然后通过对象直接或者self间接获取被封装的内容。类中封装了字段和方法,对象中封装了普通字段的值。
- 防止数据被随意修改;
- 使外部程序不需要关注对象内部的构造,只需要通过此对象对外提供的接口进行直接访问即可。
继承:对于面向对象的继承来说,其实就是将多个类共有的方法提取到父类中,子类仅需继承父类而不必一一实现每个方法。即通过父类,子类以最小代码量的方式实现不同角色的共同点和不同点的同时存在。
- 如果类中不定义__init__,调用父类super class的init
- 如果类继承父类,同时也需要定义自己的init,就需要在当前类的init调用一下父类的init
- super().__init__(参数)
- super(类名,self).__init__(参数)
- 方法搜索规则:先找当前类,再去找父类
- 重写 override:父类提供的方法不能满足子类的需求,就需要在子类中定义一个同名的方法
- 子类的方法中也可以调用父类的方法:super().方法名(参数)
- 查看搜索顺序:类名.__mro__ 或 import inspect inspect.getmro(类名)
- 父类定义的私有属性,子类获取不到
class Person(object): def __init__(self, name,age): self.name=name self.age=age class BlackPerson(Person): def __init__(self, nation): # 先继承,再重构 self.nation=nation Person.__init__(self,name,age) b = BlackPerson('A',22,'blabla') # Person.__init__(self,name,age) 这里的self相当于b,b.name='A',b.age=22 # 继承方法二:新式类写法 super(BlackPerson, self).__init__(name,age)
实例
class SchoolMember(object): members = 0 #初始学校人数为0 def __init__(self,name,age): self.name = name self.age = age def tell(self): for k,v in self.__dict__: print(k,v) def enroll(self): '''注册''' SchoolMember.members +=1 print("\033[32;1mnew member [%s] is enrolled,now there are [%s] members.\033[0m " %(self.name,SchoolMember.members)) def __del__(self): '''析构方法''' print("\033[31;1mmember [%s] is dead!\033[0m" %self.name) class Teacher(SchoolMember): def __init__(self,name,age,course,salary): super(Teacher,self).__init__(name,age) self.course = course self.salary = salary self.enroll() def teaching(self): '''讲课方法''' print("Teacher [%s] is teaching [%s] for class [%s]" %(self.name,self.course,'s12')) class Student(SchoolMember): def __init__(self, name,age,grade,sid): super(Student,self).__init__(name,age) self.grade = grade self.sid = sid self.enroll() if __name__ == '__main__': pass
多继承:Python的类如果继承了多个类,那么其寻找方法的方式有两种,分别是:深度优先和广度优先
经典类与新式类:当前类或者父类继承了object类,那么该类便是新式类,否则便是经典类。
class 类名(object): 新式类,使用super来继承父类属性, super(BlackPerson, self).__init__(name,age)
class 类名: 经典类,使用类名.__init__()继承父类属性, Person.__init__(self,name,age)
继承查找方式:
- python3:广度查找
- python2:经典类,深度查找;新式类,广度查找
经典类多继承:深度查找
#-*-coding:utf-8 -*- class D: def bar(self): print ('D.bar') class C(D): def bar(self): print ('C.bar') class B(D): def bar(self): print ('B.bar') class A(B, C): def bar(self): print ('A.bar') a = A() # 执行bar方法时 # 首先去A类中查找,如果A类中没有,则继续去B类中找,如果B类中么有,则继续去D类中找,如果D类中么有,则继续去C类中找,如果还是未找到,则报错 # 所以,查找顺序:A --> B --> D --> C # 在上述查找bar方法的过程中,一旦找到,则寻找过程立即中断,便不会再继续找了 a.bar()
新式类多继承:广度查找
class D(object): def bar(self): print ('D.bar') class C(D): def bar(self): print ('C.bar') class B(D): def bar(self): print ('B.bar') class A(B, C): def bar(self): print ('A.bar') a = A() # 执行bar方法时 # 首先去A类中查找,如果A类中没有,则继续去B类中找,如果B类中么有,则继续去C类中找,如果C类中么有,则继续去D类中找,如果还是未找到,则报错 # 所以,查找顺序:A --> B --> C --> D # 在上述查找bar方法的过程中,一旦找到,则寻找过程立即中断,便不会再继续找了 a.bar()
通过组合实现代码扩展
class Person(object): def __init__(self, name,age): self.name=name self.age=age class BlackPerson(Person): def __init__(self, nation,name,age,person_obj): # 先继承,再重构 self.nation=nation self.person=person_obj #实例化组合实现扩展
多态:接口重用,一种接口,多种实现。
class F1: pass class S1(F1): def show(self): print ('S1.show') class S2(F1): def show(self): print ('S2.show') def Func(obj): print (obj.show()) s1_obj = S1() Func(s1_obj) s2_obj = S2() Func(s2_obj)
实例化
类 =》实例化 =》 实例对象
obj = 类()
obj.函数1()
obj是对象,实例化的过程,通过对象去访问类中的函数
类的成员
所有成员中,只有普通字段的内容保存对象中,即:根据此类创建了多少对象,在内存中就有多少个普通字段。而其他的成员,则都是保存在类中,即:无论对象的多少,在内存中只创建一份。
构造函数__init__
self.name = name # 属性,成员变量,普通字段(对象可以访问;类内部可以访问;派生类中可以访问)
公有属性(静态字段):在类里直接定义的属性即为公有属性,所有实例化后的实例对象都能访问的属性。每个对象中保存相同的东西时,可以使用静态字段。
修改共有属性
类名.公有属性名 = value # 改变所有实例的属性值 实例名.公有属性名 = value # 改变单个实例的属性值
注:实例化对象时,实例内存中没有存储公有属性,访问公有属性会直接通过类的内存中寻找;当使用实例名.公有属性 = value后,会在实例内存中生成一个公有属性名的变量,以后访问直接访问实例内存的值。前者相当于全局变量,后者为局部变量。
普通字段:在__init__里定义的属性为成员属性
私有属性:__private_attr_name = value; 私有成员命名时,前两个字符是下划线;仅类内部可以访问
访问私有属性
# 对外提供只读访问接口 def get_private_attr_name(self): return self.__private_attr_name # 强制访问私有属性: 实例化名._类名__私有属性名
析构方法
在引用完成后,即实例被清除后(例如,del 实例名),执行析构方法,清空在内存中保存的实例信息。比如在server断开连接后,执行连接close等方法。
定义:def __del__(self)
类成员:方法
- 普通方法:由对象调用;至少一个self参数;执行普通方法时,自动将调用该方法的对象赋值给self;
- 类方法: 由类调用; 至少一个cls参数;执行类方法时,自动将调用该方法的类复制给cls;
- 静态方法:由类或对象调用;无默认参数;
公有方法:在类中定义的方法为公有方法,缩进和公有属性一个级别。可以在类外面定义一个方法,然后通过实例名.方法名 = 新定义的方法名来实现。
静态方法
定义:@staticmethod
- 在类中的一个方法,只是名义上归类管理,但是和类基本没太大关系,只是通过类名或者实例名调用,在静态方法中访问不了类或实例中的任何属性;
- self参数的区别,如果静态方法有参数self,调用时需要把实例当做self传入。
- 制作工具包时,可以使用,工具包中的各种方法可以没什么关联,例如os模块。
类方法:只能访问类变量,不能访问实例变量。
定义:@classmethod
class Foo: def __init__(self, name): self.name = name def ord_func(self): """ 定义普通方法,至少有一个self参数 """ print('普通方法') @classmethod def class_func(cls): """ 定义类方法,至少有一个cls参数 """ print('类方法') @staticmethod def static_func(): """ 定义静态方法 ,无默认参数""" print ('静态方法') # 调用普通方法 f = Foo('name') f.ord_func() # 调用类方法 Foo.class_func() # 调用静态方法 Foo.static_func() f.static_func()
注:self就是调用当前方法的对象,类的方法保存类内存中,实例不保存类的方法,调用类方法时相当于实例指针指向类;实例化时,成员属性(普通属性)保存在实例内存中,公有属性(静态属性)保存在类内存中,可以节省内存。(静态字段的使用场景,每个对象中保存相同的东西时,可以使用静态属性)
属性方法
定义:在普通方法的基础上添加 @property 装饰器;属性仅有一个self参数;调用时,无需括号;把一个方法变成一个静态属性。
- 不能直接给属性方法赋值,需要使用@属性方法名.setter重新定义属性方法,并且赋值。
- 删除属性,使用@属性方法名.deleter重新定义属性方法,并且赋值。
class Foo: def func(self): print('this is method test') # 定义属性 @property def prop(self): print("this is property") return 'test property' # ############### 调用 ############### foo_obj = Foo() foo_obj.func() print(foo_obj.prop ) #调用属性 ''' this is method test this is property test property '''
经典类中的属性只有一种访问方式,其对应被 @property 修饰的方法;新式类中的属性有三种访问方式,并分别对应了三个被@property(获取)、@方法名.setter(修改)、@方法名.deleter(删除)修饰的方法。
class Goods(object): def __init__(self): # 原价 self.original_price = 100 # 折扣 self.discount = 0.8 @property def price(self): # 实际价格 = 原价 * 折扣 new_price = self.original_price * self.discount return new_price @price.setter def price(self, value): self.original_price = value @price.deleter def price(self): del self.original_price obj = Goods() print(obj.price ) # 获取商品价格 obj.price = 200 # 修改商品原价 print(obj.price) del obj.price # 删除商品原价
静态字段方法
静态字段方法:创建值为property对象的静态字段
由于静态字段方式创建属性具有三种访问方式,我们可以根据他们几个属性的访问特点,分别将三个方法定义为对同一个属性:获取、修改、删除。
class Goods(object): def __init__(self): # 原价 self.original_price = 100 # 折扣 self.discount = 0.8 def get_price(self): # 实际价格 = 原价 * 折扣 new_price = self.original_price * self.discount return new_price def set_price(self, value): self.original_price = value def del_price(self): del self.original_price PRICE = property(get_price, set_price, del_price, 'description...') obj = Goods() print(obj. PRICE ) # 获取商品价格 obj. PRICE = 200 # 修改商品原价 print(obj.PRICE) print(obj.PRICE.__doc__) del obj. PRICE # 删除商品原价
property的构造方法中有个四个参数:
- 第一个参数是方法名,调用对象.属性时,自动触发执行方法
- 第二个参数是方法名,调用对象.属性=xx 时,自动触发执行方法
- 第三个参数是方法名,调用del 对象.属性时,自动触发执行方法
- 第四个参数是字符串,调用对象.属性.__doc__ ,此参数是该属性的描述信息
类的特殊成员
__doc__:表示类的描述信息
class Foo(object): """ description... """ def func(self): pass a = Foo() print (a.__doc__) # description...
__moudle__:表示当前操作的对象在哪个模块
__class__:表示当前操作的对象的类是什么
# moudle file class Foo(object): """ description... """ def func(self): pass # import file from lib.test import Foo obj = Foo() print(obj.__module__) print(obj.__class__) # lib.test # <class 'lib.test.Foo'>
__init__:构造方法,通过类创建对象时,自动触发执行。
__del__:析构方法
当对象在内存中被释放时,自动触发执行;此方法一般无须定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给Python解释器来执行,所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的。
__call__:对象后面加括号,触发执行
class Foo: def __init__(self): pass def __call__(self, *args, **kwargs): print('__call__') obj = Foo() # 执行 __init__ obj() # 执行 __call__ #构造方法的执行是由创建对象触发的,即:对象 = 类名() ;而对于 __call__ 方法的执行是由对象后加括号触发的,即:对象() 或者 类()()
__dict__:类或对象中的所有成员
class Province(object): country = 'China' def __init__(self, name, count): self.name = name self.count = count def func(self, *args, **kwargs): pass # 获取类的成员,即:静态字段、方法、 print (Province.__dict__) # {'__dict__': <attribute '__dict__' of 'Province' objects>, 'country': 'China', '__doc__': None, # '__module__': '__main__', 'func': <function Province.func at 0x0000000000A2E598>, # '__init__': <function Province.__init__ at 0x0000000000A2E510>, # '__weakref__': <attribute '__weakref__' of 'Province' objects>} obj1 = Province('Jiangxi',100000) print (obj1.__dict__) # {'name': 'Jiangxi', 'count': 100000} obj2 = Province('HuNan', 200000) print(obj2.__dict__) # {'name': 'HuNan', 'count': 200000}
__str__:如果一个类中定义了__str__方法,那么在打印对象时,默认输出该方法的返回值。
class Province(object): country = 'China' def __init__(self, name, count): self.name = name self.count = count def __str__(self): return '__str__: %s' % self.name obj1 = Province('Jiangxi',100000) print(obj1) #__str__: Jiangxi
__getitem__、__setitem__、__delitem__(用于索引操作,如字典。以上分别表示获取、设置、删除数据)
class Foo(object): def __init__(self ): self.data = {} def __getitem__(self, key): print('__getitem__ ',key) return self.data.get(key) def __setitem__(self, key, value): print('__setitem__ ',key,value) self.data[key] = value def __delitem__(self, key): #可以用来判断是否删除 print('__delitem__',key) del self.data[key] obj = Foo() obj['name'] = 'Greath' print(obj['name'])
__new__:用于创建实例,在实例化时,先执行__new__(cls,*args,**kwargs),然后执行__init__(self),如果需要自定义实例化内容时,需要用到__new__(),然后return object.__new__(cls), cls相当于类名
__metaclass__:用来表示该类由 谁 来实例化创建,用于关联原类(__metaclass__ = Mytype),在自定义类时,可以通过自定义原类中的__call__()方法,__new__()是通过原类中的__call__方法来创建的。
# -*-coding:utf-8 -*- ''' class Foo(object): def __init__(self): pass obj = Foo() print(type(obj)) # <class '__main__.Foo'> print(type(Foo)) # <class 'type'> # obj 是通过 Foo 类实例化的对象,其实,不仅 obj 是一个对象,Foo类本身也是一个对象, # 因为在Python中一切事物都是对象。 # obj对象是Foo类的一个实例,Foo类对象是 type 类的一个实例,即:Foo类对象 是通过type类的构造方法创建。 # 通过type创建类: 类是由 type 类实例化产生 def func(self): pass Foo = type('Foo', (object,), {'func': func}) # type第一个参数:类名 # type第二个参数:当前类的基类 # type第三个参数:类的成员 ''' class MyType(type): def __init__(self, what, bases=None, dict=None): super(MyType, self).__init__(what, bases, dict) print('Mytype init') def __call__(self, *args, **kwargs): print('Mytype call') obj = self.__new__(self, *args, **kwargs) self.__init__(obj) class Foo(object): __metaclass__ = MyType def __init__(self): # self.name = name print('Foo init') def __new__(cls, *args, **kwargs): print('Foo new') return object.__new__(cls, *args, **kwargs) # 第一阶段:解释器从上到下执行代码创建Foo类 # 第二阶段:通过Foo类创建obj对象 obj = Foo() # # Foo new # Foo init #python2和python3区别: 在python2中输出情况:Mytype init,Mytype call,Foo new,Foo init
注:
类的生成调用顺序依次是 __init__ (原类init)--> __call__(原类call) --> __new__(新类new)-->__init__(新类init)
metaclass 详解文章:http://stackoverflow.com/questions/100003/what-is-a-metaclass-in-python 得票最高那个答案写的非常好
反射
通过字符串的形式调用对象的相关成员,由以下4个内置函数实现:
- hasattr(obj,str_attr):判断obj是否含有以str_attr字符串对应的方法或属性,返回True or False
- getattr(obj, str_attr):获取 obj 的 str_attr,返回obj.str_attr的地址
- setattr(obj, str_attr, value):设置一个新属性,相当于obj.str_attr = value
- delattr(obj, str_attr):删除属性str_attr
# -*-coding:utf-8 -*- class Foo(object): def __init__(self): self.name = 'greath' def func(self): print('this is func')
obj = Foo() # #### 检查是否含有成员 #### print(hasattr(obj, 'name')) # True print(hasattr(obj, 'func')) # True # #### 获取成员 #### print(getattr(obj, 'name')) # greath getattr(obj, 'func')() # this is func # #### 设置成员 #### setattr(obj, 'age', 18) setattr(obj, 'show', lambda num: num + 1) print(obj.age) # 18 print(obj.show(10)) # 11 choice = input('>>') setattr(obj, choice, 'CH') # CH print(getattr(obj,choice)) #print(obj.choice) # AttributeError: 'Foo' object has no attribute 'choice' # #### 删除成员 #### delattr(obj, 'name') #print(obj.name) # AttributeError: 'Foo' object has no attribute 'name'