面向对象三大特性之多态
多态是面向对象语言的一个基本特性,多态意味着变量并不知道引用的对象是什么,根据引用对象的不同表现不同的行为方式。它是一种机制、一种能力,而非某个关键字。它在类的继承中得以实现,在类的方法调用中得以体现。多态意味着变量并不知道引用的对象是什么。
例子1.
1 a=34 2 b=57 3 print(a+b) 4 5 a="世界" 6 b="你好" 7 print(a+b)
注:我们不知道+法运算符左右两个变量是什么类型,当我们给的是int类型时,它就进行加法运算。当我们给的是字符串类型时,它就返回的是两个字符串拼接的结果。也就是根据变量类型的不同,表现不同的形态。
例子2.
1 main.py 2 class person: 3 def say(self): 4 print("大家好") 5 6 7 class student: 8 def say(self): 9 print("老师好") 10 11 12 index.py 13 from random import choice 14 import main2 15 16 p1 = main2.person() 17 s1 = main2.student() 18 obj = choice([p1, s1]) 19 obj.say()
注:我们创建的临时对象obj是由随机函数取出来的,我们不知道它的具体类型,但是我们可以对它进行相同的操作。即让它调用say方法,然后根据其类型的不同,它所表现的行为不同。这就是多态。
面向对象中的成员
字段:
静态字段:
静态字段用类访问(万不得已时可以用对象访问)
静态字段在创建类时就存在
1 class Province: 2 def __init__(self, name): 3 self.name = name 4 self.contry = '中国' 5 6 sc = Province('河南') 7 sd = Province('山东') 8 9 #上面代码每个对象都属于中国,如果有1000个对象,那么保存了1000个中国,浪费内存.使用静态字段优化 10 11 class Province: 12 contry = '中国' 13 def __init__(self, name): 14 self.name = name 15 16 @staticmethod 17 def f1(arg1, arg2): 18 print(arg1, arg2) 19 20 @classmethod 21 def f2(cls): 22 print(cls) 23 24 sc = Province('河南') 25 sd = Province('山东') 26 print(sc.name) 27 print(sc.contry)
普通字段:
普通字段只能对象访问
普通字段必须要创建对象之后才存在
方法:
静态方法:
静态方法在创建类时就在内存中存在
静态方法没有self,定义静态方法时先写@staticmethod关键字,由类调用执行
普通方法:
由对象去调用执行(方法属于类)
普通方法必须要创建对象之后才在内存中存在
类方法:
没有self参数,必须有cls参数,和self参数一样,使用@classmethod关键字定义,自动传递.是静态方法的一种,由类调用执行
属性:
- 让访问类方法以字段的方式访问
- 使用@property关键字定义.
- 不伦不类的东西,具有方法的写作形式,具有字段的访问形式
- @方法名.setter,让方法支持字段的设置
- @方法名.deleter,让方法支持字段的删除
1 class pager: 2 3 def __init__(self, all_count): 4 self.all_count = all_count 5 6 @property 7 def all_pager(self): 8 a1, a2 = divmod(self.all_count, 10) 9 if a2 == 0: 10 return a1 11 else: 12 return a1 + 1 13 @all_pager.setter 14 def all_pager(self, value): 15 print(value) 16 17 @all_pager.deleter 18 def all_pager(self): 19 print('del all_pager') 20 21 22 p = pager(100) 23 ret = p.all_pager #调用all_pager方法,没有括号,具有字段的访问形式 24 p.all_pager = 200 #调用all_pager.setter 25 del p.all_pager #调用all_pager_deleter 26 print(ret)
1 class pager: 2 3 def __init__(self, all_count): 4 self.all_count = all_count 5 6 def f1(self): 7 return 123 8 9 def f2(self, value): 10 pass 11 12 def f3(self): 13 pass 14 15 foo = property(fget=f1, fset=f2, fdel=f3) 16 17 p = pager(200) 18 result = p.foo 19 p.foo = 123 20 del p.foo
特殊成员:
- __init__,构造方法,在对象初始化的时候自动执行
- __del__,析构方法,在对象被python垃圾回收器回收之前执行
1 class Foo: 2 3 def __init__(self, name, age): 4 self.name = name 5 self.age = age 6 7 def __del__(self): 8 print("我要被杀死了...") 9 10 def __call__(self): 11 print('i am call method.') 12 13 obj1 = Foo('gouyacai', 18) 14 del obj1 15 16 结果: 17 我要被杀死了...
- __doc__,类的注释信息
1 class Foo: 2 """ 3 this is class notes 4 """ 5 6 print(Foo.__doc__)
- __module__和__class_,说明当前对象在哪个模块和类名是什么
1 class Foo: 2 def __init__(self, name, age): 3 self.name = name 4 self.age = age 5 6 def __call__(self): 7 print('i am call method.') 8 9 obj1 = Foo('gouyacai', 18) 10 print(obj1.__class__,Foo.__module__) 11 12 结果: 13 <class '__main__.Foo'> __main__
- __call__,类名加括号表示执行类的__init__方法,对象名加括号就是执行__call__方法,或者类名加两个括号表示先执行__init__方法,在执行__call__方法
1 class Foo: 2 def __call__(self): 3 print('i am call method.') 4 5 obj = Foo() 6 obj() 7 Foo()()
- __str__,str方法返回什么,打印对象本身就输出什么
1 class Foo: 2 def __init__(self, name): 3 self.name = name 4 5 def __str__(self): 6 return self.name 7 8 obj = Foo('gouyacai') 9 print(obj)
- __dict__,获取当前对象(类)中封装的字段,以字典的方式返回
1 class Foo: 2 3 def __init__(self, name, age): 4 self.name = name 5 self.age = age 6 7 def __call__(self): 8 print('i am call method.') 9 10 obj1 = Foo('gouyacai', 18) 11 print(obj1.__dict__) 12 print(Foo.__dict__)
- __getitem__,dic['name'] 通过中括号自动执行此函数,字典操作中的获取value的原理就是这个特殊成员实现的
1 class Bar: 2 def __getitem__(self, item): 3 print(item) 4 5 obj = Bar() 6 obj[123] 7 8 #切片的应用:传的什么数据类型,item就是什么数据类型 9 class Bar: 10 def __getitem__(self, item): 11 print(type(item), item, item.start, item.stop, item.step) 12 13 14 obj = Bar() 15 obj[1:2:6]
- __setitem__,dic['name'] = 'gouyacai' 通过中括号自动执行此函数,字典中的赋值操作就是这个特殊成员实现的
1 class Bar: 2 def __setitem__(self, key, value): 3 print(key, value) 4 5 obj = Bar() 6 obj['hello'] = 'world' 7 8 #切片的应用:传的什么数据类型,key和value就是什么数据类型 9 class Bar: 10 def __setitem__(self, key, value): 11 print(type(key), type(value), key.start, key.stop, key.step) 12 13 14 obj = Bar() 15 obj[1:2:5] = [11,22,33]
- __delitem__,del dic['name']通过中括号自动执行此函数,字典中的删除key就是这个特殊成员实现的
1 class Bar: 2 def __delitem__(self, key): 3 print(key) 4 5 obj = Bar() 6 del obj[123] 7 8 #切片的应用:传的什么数据类型,key就是什么数据类型 9 class Bar: 10 def __delitem__(self, key): 11 print(type(key), key, key.start, key.stop, key.step) 12 13 obj = Bar() 14 del obj[1:2:4]
- __iter__,要让对象可以被迭代,需要在类中定义iter方法,并且返回一个iter类型的对象
1 class Foo: 2 def __iter__(self, list): 3 return iter(list) 4 5 obj = [11,22,33,44,55,66] 6 7 for i in obj: 8 print(i)
成员修饰符
公有
类成员默认都是公有的
私有
- 在类成员前面加上“__”会将类成员变成私有的,私有的字段,属性和方法只能在类内部调用,类外部和对象不能访问和调用
- 私有成员也不能被继承
- 静态字段如果变成私有,那么内部访问私有字段,要把方法变成静态方法才能访问
- 如果非要访问,也他妈的可以。。。obj._foo__name(obj是对象,foo是类名,name是私有字段。)
1 class Foo: 2 __age = 18 #私有字段 3 def __init__(self, name): 4 self.__name = name 5 6 @staticmethod #如果要访问私有字段,要将方法变成静态方法 7 def f1(): 8 print(Foo.__age) #获取私有字段 9 10 def f2(self): 11 print(self.__name) 12 13 def __f3(self): #定义私有方法 14 print('hello,world') 15 16 def f4(self): 17 return self.__f3() #私有方法只能在类内部调用 18 19 obj = Foo('gouyacai') 20 Foo.f1() 21 obj.f2() 22 obj.f3() #错误的,f3是私有方法,不能在类的外部调用访问 23 obj.f4()
面向对象其他
1、isinstance():判断一个对象是否是一个类的实例
1 class Foo: 2 def __init__(self, name, age): 3 self.name = name 4 self.age = age 5 6 def __call__(self): 7 print('i am call method.') 8 9 class Bar: 10 def __init__(self, number, sex): 11 self.number = number 12 self.sex = sex 13 14 obj1 = Foo('gouyacai', 18) 15 obj2 = Bar(520, '男') 16 print(isinstance(obj1, Foo)) 17 print(isinstance(obj1, Bar)) 18 19 结果: 20 False 21 True
2、issubclass():判断一个类是否是另一个类的子类
1 class Foo: 2 def __init__(self, name, age): 3 self.name = name 4 self.age = age 5 6 def __call__(self): 7 print('i am call method.') 8 9 class Bar(Foo): 10 def __init__(self, number, sex): 11 self.number = number 12 self.sex = sex 13 14 obj1 = Foo('gouyacai', 18) 15 obj2 = Bar(520, '男') 16 print(issubclass(Bar, Foo)) 17 print(isinstance(Foo, Bar))
3、执行父类的方法(super方法)
1 class C1: 2 def f1(self): 3 print('C1.f1') 4 5 class C2(C1): 6 def f1(self): 7 super(C2, self).f1() # 执行自己的f1时,先执行父类的f1方法 8 print('C2.f1') 9 10 obj = C2() 11 obj.f1()
4、重写字典类,使其成为有序的
1 class Mydict(dict): 2 3 def __init__(self): 4 self.li = [] 5 super(Mydict, self).__init__() 6 7 def __setitem__(self, key, value): 8 self.li.append(key) 9 super(Mydict, self).__setitem__(key, value) 10 11 def __str__(self): 12 temp_list = [] 13 for key in self.li: 14 value = self.get(key) 15 temp_list.append("'%s':%s" %(key, value)) 16 temp_str = "{" + ",".join(temp_list) + "}" 17 return temp_str 18 19 obj = Mydict() 20 21 obj['k1'] = 123 22 obj['k2'] = 456 23 obj['k3'] = 789 24 print(obj)
设计模式之(单例模式)
单例模式是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例的特殊类。通过单例模式可以保证系统中一个类只有一个实例。
单例模式的应用场景:
对于系统中的某些类来说,只有一个实例很重要,例如,一个系统中可以存在多个打印任务,但是只能有一个正在工作的任务;一个系统只能有一个窗口管理器或文件系统;一个系统只能有一个计时工具或ID(序号)生成器。如在Windows中就只能打开一个任务管理器。如果不使用机制对窗口对象进行唯一化,将弹出多个窗口,如果这些窗口显示的内容完全一致,则是重复对象,浪费内存资源;如果这些窗口显示的内容不一致,则意味着在某一瞬间系统有多个状态,与实际不符,也会给用户带来误解,不知道哪一个才是真实的状态。因此有时确保系统中某个对象的唯一性即一个类只能有一个实例非常重要。如何保证一个类只有一个实例并且这个实例易于被访问呢?定义一个全局变量可以确保对象随时都可以被访问,但不能防止我们实例化多个对象。一个更好的解决办法是让类自身负责保存它的唯一实例。这个类可以保证没有其他实例被创建,并且它可以提供一个访问该实例的方法。这就是单例模式的模式动机。
单列模式的要点:
- 一是某个类只能有一个实例;
- 二是它必须自行创建这个实例;
- 三是它必须自行向整个系统提供这个实例。
单例模式的实现要点
- 一是单例模式的类只提供私有的构造函数;
- 二是类定义中含有一个该类的静态私有对象;
- 三是该类提供了一个静态的公有的函数用于创建或获取它本身的静态私有对象;
1 class Foo: 2 instance = None 3 4 def __init__(self, name): 5 self.name = name 6 7 @classmethod 8 def get_instance(cls): 9 if cls.instance: 10 return cls.instance 11 else: 12 obj = cls('gouyacai') 13 cls.instance = obj 14 return obj 15 16 obj1 = Foo.get_instance() 17 print(obj1) 18 obj2 = Foo.get_instance() 19 print(obj2)
单例模式的优缺点
优点
- 实例控制,单例模式会阻止其他对象实例化其自己的单例对象的副本,从而确保所有对象都访问唯一实例。
- 灵活性,因为类控制了实例化过程,所以类可以灵活更改实例化过程。
缺点
- 开销,虽然数量很少,但如果每次对象请求引用时都要检查是否存在类的实例,将仍然需要一些开销。可以通过使用静态初始化解决此问题。
- 开发混淆,使用单例对象(尤其在类库中定义的对象)时,开发人员必须记住自己不能使用new关键字实例化对象。因为可能无法访问库源代码,因此应用程序开发人员可能会意外发现自己无法直接实例化此类。
- 对象生存期,不能解决删除单个对象的问题。在提供内存管理的语言中(例如基于.NET Framework的语言),只有单例类能够导致实例被取消分配,因为它包含对该实例的私有引用。在某些语言中(如 C++),其他类可以删除对象实例,但这样会导致单例类中出现悬浮引用。。
1 class Foo: 2 instance = None 3 4 def __init__(self, name): 5 self.name = name 6 7 @classmethod 8 def get_instance(cls): 9 if cls.instance: 10 return cls.instance 11 else: 12 obj = cls('gouyacai') 13 cls.instance = obj 14 return obj 15 16 obj1 = Foo.get_instance() 17 print(obj1) 18 obj2 = Foo.get_instance() 19 print(obj2)