python面向对象编程
面向过程的编程思想
指的是解决问题的步骤,类似设计一条流水线,是一种机械式的思维方式
优点:复杂的问题流程化,简单化
缺点:可扩展性差
面向对象的编程思想
对象就是特征和技能的结合体,
(个人理解:操作一个个对象,给它方法和属性,让它完成某个目标,不去关注它完成目标的过程)
优点:可扩展性强
缺点:编程的复杂度要高与面向过程
1、类
对象是特征与技能的结合体,而类则是一系列对象相同特征与技能的结合体
强调:
1. 对象是具体存在的事物,而类则是一个抽象的概念
2. 站在不同的角度总结出的类与对象不同
在程序中:先定义类,后调用类产生对象
(个人理解:类,同类 ,相同的属性, 一种抽象的概念,把一个个对象中具有的相同属性和方法,
拿出来,单独放在一个地方,这个地方就是类)
# 定义类 class Student: # 相同的特征 school = "NO.4 school" # 相同的技能 def study(self): print("good good study.") # 类是一系列对象相同特征(变量)和技能(函数的结合体,即类体中就是变量和函数) # 但类体中可以存在任意python代码 # 类体代码会再类定义阶段立即执行,会产生一个类名称空间,用来将执行过程中产生的名字都丢进去 # 查 print(Student.__dict__)# 查看类的名称空间 print(Student.school) # print(Student.__dict__['school']) print(Student.study) # 修改 Student.school = "University" # Student.__dict__['school'] = 'University' Student.country = "China" # Student.__dict__["country"] = "China" del Student.country # del Student.__dict__["country"] # 技能(函数)的使用 Student.study(123)
总结:
1. 类的本质就是一个名称空间,存放着变量名和函数名
2. 用途:当作名称空间从内部取出名字来使用,或调用类产生对象
2、对象
调用类产生对象,对象的本质也是名称空间
调用类的过程称之为类的实例化,调用类的返回值称之为类的一个对象/实例
class Student: # 相同的特征 school = "NO.4 school" def __init__(self, name, age, sex): self.name = name self.age = age self.sex = sex # 相同的技能 def study(self): print("good good study.") stu1 = Student('sun', 22, 'boy') # 调用类后发生了: # 1. 先产生一个空对象stu1,然后返回 # 2. 触发类中函数__init__的执行,将对象本身同调用类括号内指定的参数一同传入__init__内 # __init__的功能:是在实例化时就为对象初始自己独有的特征 # 注意:不能有返回值,不然报错
属性的查找顺序:
先从对象中查找,没有,就到类中查找,还没有就报错
类中定义的属性是所有对象共享的,对象可以来用,类也可以用
对象修改类定义的属性,结果是,在自己的名称空间内重新添加了一个新的属性
类修改自己定义的属性,结果是,所有对象调用时,数据都会发生改变
# 统计实例了对象的个数 class Student: # 相同的特征 school = "NO.4 school" count = 0 def __init__(self, name, age, sex): self.name = name self.age = age self.sex = sex Student.count += 1 # 相同的技能 def study(self): print("good good study.") stu1 = Student('aa', 12, 'boy') stu2 = Student('bb', 12, 'girl') stu3 = Student('cc', 15, 'boy') print(Student.count, stu1.count, stu2.count, stu3.count) # 结果是:3 3 3 3
绑定方法:
类中定义的函数是类的函数属性,类可以使用,但是绑定给对象用的
class Student: # 相同的特征 school = "NO.4 school" def __init__(self, name, age, sex): self.name = name self.age = age self.sex = sex # 相同的技能 def study(self, x): print("good good study.%s"%self.name) stu1 = Student('aa', 12, 'boy') # 类来使用 # Student.study(stu1,123) # 对象调用 # 对象调用时,会把自己作为第一个参数传入 # 若需要传其它参数,可以直接在括号内填入 stu1.study(123)
3、继承与派生
继承是一种新建类的方式,新建的类称之为子类/派生类,被继承的类称之为父类/基类/超类
python中继承特点:
1. 子类可以重用父类的属性
2. python中一个子类可以同时继承多个父类
3. 在继承背景下,类分为新式类,经典类
新式类:但凡继承了object的类以及类的子类
python3中一个类即便没有显示继承任何类,默认会继承object
即python3中所有的类都是新式类
经典类: 没有继承object类以及该类的子类
在python2中才区分新式类与经典类
在python2中一个类如果没有显示地继承任何类,也不会继承object
为什么要用继承:减少类与类之间的代码冗余
查看自己有哪些父类:__bases__
# 第一种方式:指名道姓地引用某一个类地函数 # 使用这种方式,和继承无关 # 访问的是类的函数,没有自动传值的效果 class People: school = 'No.4 school' def __init__(self,name,age,sex): self.name = name self.age = age self.sex = sex class Student(): def __init__(self, name, age, sex, score=0): # 使用父类的属性 People.__init__(self,name,age,sex) # 自己派生新的属性 self.score = score def study(self): print('%s is studying.' % self.name) class Teacher(): def __init__(self,name,age,sex,level): People.__init__(self,name,age,sex) self.level=level def score(self,stu,num): stu.score=num stu1=Student('sun',28,'male') print(stu1.__dict__) stu1.study() tea1=Teacher('aaa',18,'male',10) print(tea1.__dict__)
# 第二种方式:super()必须在类中使用(使用了后面的MRO列表) # 在python2中,super(自己的类名, 自己的对象) # 在python3中,super() # 调用该函数会得到一个特殊的对象,该对象专门用来访问父类中的属性!!!完全参照mro列表!!! # 不会根据有没有实际的继承关系 # 总结: # 1. 严格依赖继承的mro列表 # 2. 访问是绑定方法,有自动传值的效果 class A: def f1(self): print('A.f1') super().f2() class B: def f2(self): print('B.f2') class C(A,B): def f2(self): print('C.f2') obj=C() print(C.mro()) obj.f1() ''' [<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>] A.f1 B.f2 ''' # 两种方式最好不要混着用
1. 单继承下属性查找
优先级:对象-> 对象的类->父类->....->object
class Foo: def f1(self): print('Foo.f1') def f2(self): print('Foo.f2') self.f1() #obj.f1() class Bar(Foo): def f1(self): print('Bar.f1') obj=Bar() obj.f2() ''' Foo.f2 Bar.f1 '''
2. 多继承下的属性查找
如果一个子类继承多个分支(分支没有共同继承一个非object的类)
这时新式类和经典类的查找顺序是一样的。
此时属性的查找优先级是:对象—对象的类—按照从左到右的顺序一个分支一个分支的找

# # 第四层: # class G: # # x = 'G' # pass # # # 第三层 # class E(G): # # x = 'E' # pass # # class F: # # x = 'F' # pass # # # 第二层 # class B(E): # # x = 'B' # pass # # class C(F): # # x = 'C' # pass # # class D: # # x = 'D' # pass # # # 第一层 # class A(B, C, D): # # x = 'A' # pass # # obj = A() # # obj.x = 111 # print(obj.x)
如果出现菱形继承:
新式类:广度优先查找,从左到右一个分支一个分支查找,在最后一个分支才去查找顶级类
经典类:深度优先查找,从左到右一个分支一个分支的查找,在第一个分支就查找顶级类

#对于新式类,python会通过C3线性算法计算出一个MRO表, # 表中是一个简单的所有基类的线性顺序列表,如: >>> A.mro() #等同于A.__mro__ [<class '__main__.A'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.F'>, <class '__main__.D'>, <class '__main__.G'>, <class 'object'>] MRO遵循三条准则: 1. 子类会先于父类查找 2. 多个父类会根据它们在列表中的顺序被检查 3. 如果对下一个类存在两个以上合法的选择,选择第一个类
组合
组合指的是某一个对象拥有一个属性,该属性的值是另外一个类的对象
为何使用组合:
通过某一对象添加属性(属性的值是另外一个类的对象)的方式,
可以间接的将两个类组合在一起,减少代码的冗余
4、多态
多态指的是同一种/类事物的不同形态
多态性:在多态的背景下,可以不用考虑对象具体类型的前提下而直接使用对象
多态性的精髓:统一
# 第一种方式: # 有python提供比较硬性的方式来同一接口,(不推荐使用你) import abc class Animal(metaclass=abc.ABCMeta): @abc.abstractmethod def speak(self): pass @abc.abstractmethod def run(self): pass # Animal() # 父类只是用来建立规范的,不能用来实例化的,更无需实现内部的方法 class People(Animal): def speak(self): print('say hello') def run(self): pass class Dog(Animal): def speak(self): print('汪汪汪') def run(self): pass class Pig(Animal): def speak(self): print('哼哼哼') def run(self): pass obj1=People() obj2=Dog() obj3=Pig() # 实例化对象,不会报错, # 但当你调用任何方法时,如果类中没有规定的speak,run方法时就会报错 # 这样就可以达到强制统一接口的作用
# 第二种方式 # 人为的统一接口,不做任何强制措施 # 就是python俗称的鸭子类型 class Disk: def read(self): print('Disk read') def write(self): print('Disk write') class Memory: def read(self): print('Mem read') def write(self): print('Mem write') class Cpu: def read(self): print('Cpu read') def write(self): print('Cpu write') obj1=Disk() obj2=Memory() obj3=Cpu() obj1.read() obj2.read() obj3.read()
5、封装
封:往容器/名称空间里存入名字
装:代表将存放于名称空间中的名字给藏起来,这种隐藏是对外部不对内
如何封装:
在类定义的属性或方法前加__开头(没有__结尾)
总结:
1. __开头的属性实现隐藏仅仅是一种语法意义上的变形,并不会真的限制类外部的使用
(封装的方法:__func变成了_类名__func)
2. 该变形操作只在类定义阶段时执行一次,类定义阶段后新增的__开头的属性不会变形
3. 如果父类不想子类覆盖自己的属性,可以在属性前加上__开头
class Foo: __x=111 # _Foo__x __y=222 # _Foo__y def __init__(self,name,age): self.__name=name self.__age=age def __func(self): #_Foo__func print('func') def get_info(self): print(self.__name,self.__age,self.__x) #print(self._Foo__name,self._Foo__age,self._Foo__x) # print(Foo.__x) # print(Foo.__func) # print(Foo.__dict__) # print(Foo._Foo__x) # print(Foo._Foo__y) # Foo.__z=333 # print(Foo.__dict__) # print(Foo.__z) # 不想让子类覆盖父类的方法 class Foo: def __f1(self): #_Foo__f1 print('Foo.f1') def f2(self): print('Foo.f2') self.__f1() #obj._Foo__f1() class Bar(Foo): def __f1(self): # _Bar__f1 print('Bar.f1') obj=Bar() obj.f2() ''' Foo.f2 Bar.f1 '''
为什么封装:
对数据属性的封装:将数据属性隐藏起来,类外部无法直接操作属性,需要类内部开辟一个接口,
让外部的使用可以间接地操作属性,可以在接口内定义任意的控制逻辑
class People: def __init__(self,name,age): self.__name=name self.__age=age def tell_info(self): print('<name:%s age:%s>' %(self.__name,self.__age)) def set_info(self,name,age): if type(name) is not str: print('名字必须是str类型') return if type(age) is not int: print('年龄必须是int类型') return self.__name=name self.__age=age obj=People('sun',18) obj.tell_info() # obj.set_info(123,19) obj.set_info('sun','18') # 上述两个修改都不会成功
对函数的封装:隔离复杂度
class ATM: def __card(self): print('插卡') def __auth(self): print('用户认证') def __input(self): print('输入取款金额') def __print_bill(self): print('打印账单') def __take_money(self): print('取款') def withdraw(self): self.__card() self.__auth() self.__input() self.__print_bill() self.__take_money() a=ATM() a.withdraw()
6、property装饰器
用来将类内的函数属性伪装成数据属性
# 使用 class People: def __init__(self,name): self.__name=name @property def name(self): return '<名字:%s>' %self.__name @name.setter def name(self,obj): if type(obj) is not str: print('name必须为str类型') return self.__name=obj @name.deleter def name(self): del self.__name obj=People('sun') # 查看 # print(obj.name) # 修改 # obj.name='SUN' # obj.name=123 # print(obj.name) # 删除 del obj.name print(obj.__dict__) # 以前property设置方法 class People: def __init__(self,name): self.__name=name def get_name(self): return '<名字:%s>' %self.__name def set_name(self,obj): if type(obj) is not str: print('name必须为str类型') return self.__name=obj def del_name(self): del self.__name name=property(get_name,set_name,del_name) obj=People('sun') print(obj.__dict__)
7、类中定义方法
绑定方法
绑定给谁就应该由谁来调用,谁来调用就会将谁作为第一个参数传入
绑定给对象的方法:类中定义的函数默认就是绑定给对象的
绑定给类的方法:为类中定义的函数加上一个装饰器classmethod, (对象也可以调用,
但传入的第一个参数的值任然是类,一般不会这样使用)
非绑定方法
既不与类绑定,又不与对象绑定,意味着对象和类都可以来调用,无论谁来调用都是一个普通的函数,
没有自动传值的效果,使用@staticmethod装饰器
# class Foo: # def f1(self): # print(self) # # @classmethod # def f2(cls): # print(cls) # # @staticmethod # def f3(x,y): # print('f3',x+y) # 绑定给对象的方法 # obj=Foo() # print(obj.f1) # obj.f1() # 结果是: # <bound method Foo.f1 of <__main__.Foo object at 0x000001F2732CF0F0>> # <__main__.Foo object at 0x000001F2732CF0F0> # 绑定给类的方法使用 # # print(Foo.f2) # # Foo.f2() # # print(obj.f2) # # obj.f2() # 结果是: # <bound method Foo.f2 of <class '__main__.Foo'>> # <class '__main__.Foo'> # <bound method Foo.f2 of <class '__main__.Foo'>> # <class '__main__.Foo'> # 非绑定方法的使用 # print(Foo.f3) # print(obj.f3) # Foo.f3(1,2) # obj.f3(3,4) # 结果是: # <function Foo.f3 at 0x0000023DA02F3950> # <function Foo.f3 at 0x0000023DA02F3950> # f3 3 # f3 7
# 应用 # setting.py IP = "127.0.0.1" PORT = 3306 # run.py import settings class MySql: def __init__(self, ip, port): self.id = self.create_id() self.ip = ip self.port = port def tell_info(self): print('<id:%s ip:%s port:%s>' % (self.id, self.ip, self.port)) @classmethod def from_conf(cls): return cls(settings.IP, settings.PORT) @staticmethod def create_id(): import uuid return uuid.uuid4() # obj1=MySql('1.1.1.1',3306) # obj1.tell_info() # 绑定给类的方法提供了另外一种实例化对象的方式 obj2 = MySql.from_conf() # 查看非绑定方法的结果 obj2.tell_info()
8、内置方法
1. isinstance和insubclass
isinstance 判断一个对象是否是一个类的实例,issubclass 判断是否是子类
isinstance() 与 type() 区别: type() 不会认为子类是一种父类类型,不考虑继承关系。 isinstance() 会认为子类是一种父类类型,考虑继承关系。 要判断两个类型是否相同推荐使用 isinstance() 示例: class A: pass class B(A): pass isinstance(A(), A) # returns True type(A()) == A # returns True isinstance(B(), A) # returns True type(B()) == A # returns False
class A: pass class B(A): pass issubclass(B, A) # 注:issubclass内的参数必须是一个类,不然会报错
2. 反射
通过字符串映射到对象或类的属性(主要和input连用)
# hasattr 判断对象或类是否有字符串对应的方法或属性 hasattr(obj, ;'name') # 'name' in obj.__dict__ getattr(obj, 'name') # obj.__dict__['name'] setattr(obj, 'age', 18) # obj.sex = 18 delattr(obj, 'name') print(obj.__dict__)
3. 属性attr方法
作用:如果属性查找在实例以及对应的类中(通过__dict__)失败, 那么会调用到类的__getattr__函数, 如果没有定义这个函数,
那么抛出AttributeError异常。由此可见,__getattr__一定是作用于属性查找的最后一步,兜底。
__setattr__: 添加/修改属性会触发它的执行
__delattr__: 删除属性的时候会触发
__getattr__: 只有在使用点调用属性且属性不存在的时候才会触发
4. item方法
__getitem__: 使用类似字典的方式得到数据
__setitem__:
__delitem__:
def __getitem__(self, item): return self.__dict__get(item) def __setitem__(self, key, value): self.__dict__[key]=value def __delitem__(self, key): print('del obj[key]时,我执行') self.__dict__.pop(key) obj["name"] obj["sex"] = 19 del obj["sex"]
5. __dir__()
获取对象所有的属性名和方法名(包括继承类中的属性和方法),对象使用方式a.__dir__(),类使用方式Bar.__dir__(a);执行dir(object)也是调用的对象的__dir__()方法。
class Foo: def foo(self): pass class Bar(Foo): count = 0 def __init__(self): self.name = "sun" def bar(self): pass a = Bar() print(a.__dir__()) # 结果 """ ['name', '__module__', 'count', '__init__', 'bar', '__doc__', 'foo', '__dict__', '__weakref__', '__repr__', '__hash__', '__str__', '__getattribute__', '__setattr__', '__delattr__', '__lt__', '__le__', '__eq__', '__ne__', '__gt__', '__ge__', '__new__', '__reduce_ex__', '__reduce__', '__subclasshook__', '__init_subclass__', '__format__', '__sizeof__', '__dir__', '__class__'] """
6. __dict__
1. 类的__dict__得到是类本身(不包括继承的类)相关属性和方法,对象的__dict__得到的是仅仅是对象的属性,即self.xxx
class Foo: def foo(self): pass class Bar(Foo): """this is class bar""" count = 0 def __init__(self): self.name = "sun" def bar(self): pass a = Bar() print(Bar.__dict__) print(a.__dict__) # 结果 """ {'__module__': '__main__', '__doc__': 'this is class bar', 'count': 0, '__init__': <function Bar.__init__ at 0x7f05f5aec8c8>, 'bar': <function Bar.bar at 0x7f05f5aec950>} {'name': 'sun'} """
2. 一些内置的数据类型是没有__dict__属性的,如:整数、字典、列表等
3. 如果类继承了父类的属性,类的__dict__没有变化,对象的__dict__则增加了父类的属性
class Foo: def __init__(self): self.cls = "Foo" def foo(self): pass class Bar(Foo): """this is class bar""" count = 0 def __init__(self): self.name = "sun" super(Bar, self).__init__() def bar(self): pass a = Bar() print(Bar.__dict__) print(a.__dict__) # 结果 """ {'__module__': '__main__', '__doc__': 'this is class bar', 'count': 0, '__init__': <function Bar.__init__ at 0x7f9df8c00950>, 'bar': <function Bar.bar at 0x7f9df8c009d8>} {'name': 'sun', 'cls': 'Foo'} """
4. 函数vars()和__dict__的效果一样,都是返回对象或类的属性和属性值的字典对象
7. 其它
__str__: 会在打印对象时触发,可以定义对象被打印时的输出信息,返回必须是字符串
__del__: 回收资源的操作,在对象被删掉前或程序结束后执行,可以用来回收对象意外其它相关资源
__call__: 在对象被调用时会自动触发方法
__eq__: 当两个对象作比较时a1==a2触发
class User: def __init__(self, name, age): self.name = name self.age = age def __eq__(self, obj): if self.name == obj.name and self.sex==obj.sex: return True
9、异常
异常是错误发生的信号,程序出错就会产生异常,如果该异常没有被程序处理,异常就会抛出,程序终止。
异常信息包含三部分:
traceback 异常的追踪信息
异常的类型
异常的内容
为什么要处理异常
避免程序因为异常而崩溃,所以在应用程序中应该对异常进行处理,从而增强程序的健壮性
分类:
语法上错误,在程序执行前就应该立即修正
逻辑上的错误,可提前预防的错误,使用if判断,如果不能预知,就用try,except
try: code..... except NameError as e: # e是错误信息 # 当抛出的异常是NameError时,执行该代码快 code..... except NameError2: # 多分支 code.... # 可以写在一行 except (NameError, NameError2) # 万能异常 Exception # else 当没有发现异常时,执行else内的内容 try: ..... else: .... # finally: 最后执行,无论有没有异常
# 自定义异常类型 class MyException(BaseException): def __str__(self): # 打印异常值 return "" # 报异常 raise MyException("我定义的异常")
assert 条件,条件不成立,则抛出异常
10、元类
原因:python中一切皆对象
什么是元类:
内置的元类是type,由元类实例化得到自定义的类,自定义的类实例化得到对象
自定义类的三个组成部分:
1. 类名
2. 类的基类们
3. 类的名称空间
class创建自定义类的底层原理:
1. 拿到类名
2. 拿到类的基类们
3. 拿到类的名称空间(使用exec,执行字符串)
4. 调用元类实例化得到自定义的类,
class_name = "Student" class_bases = (object,)(必须是元组) class_namespace = {} class_body = """ def __init__(self, name, age): self.name = name self.age = age def study(self): print("%s is studying"%self.name) """ exec(class_body, {}, class_namespace) Student = type(class_name, class_bases, class_namespace) stu1 = Student('sun', 22)
# 只有继承了type的类才能称为自定义元类,否则就是一个普通的类 class Mymeta(type): def __init__(cls, class_name, class_bases, class_namespace): pass class Student(metaclass=Mymeta): def __init__(self, name, age): self.name = name self.age = age def study(self): print("%s is studying"%self.name)
# 使用自定义元类给自定义类加上限制 # 类名必须是驼峰体,即首字母必须大写 # 必须有注释 class Mymeta(type): def __init__(cls, class_name, class_bases, class_namespace): # 类名必须大写 if not class_name.istitle(): raise TypeError("类名首字母必须大写") doc = class_namespace.get("__doc__") if not (doc and doc.strip()): raise TypeError("必须要有注释,且注释不能为空") class Student(metaclass=Mymeta): def __init__(self, name, age): self.name = name self.age = age def study(self): print("%s is studying"%self.name)
# 元类中__call__的使用 # 类的实例化过程即类的调用Student("sun", 22) # 1. 先产生一个空对象 # 2. 执行__init__,完成对象的初始属性操作 # 3. 返回初始化好的对象 # 因此:类的实例化即调用元类的__call__方法 class Mymeta(type): def __init__(cls, class_name, class_bases, class_namespace): # 类名必须大写 pass def __call__(self, *args,**kwargs):#self是Student obj = self.__new__(self) # 实际上使用的是:object.__new__(self) self.__init__(obj, *args, **kwargs) # 可以对新产生的对象加上限制 # 比如控制对象属性全部隐藏(隐藏后方法中若是需要使用必须是self.__name) obj.__dict__ = {("_%s__%s"%(self.__name__, k)): v for k,v in obj.__dict__.items()} return obj class Student(metaclass=Mymeta): def __init__(self, name, age): self.name = name self.age = age def study(self): print("%s is studying"%self.__name) stu1 = Student("sun", 20) stu1.study()
# 使用自定义元类后属性的查找顺序
# 先对象层:对象 -> 父类们 -> object
# 后元类层:Mymeta -> type
# 为什么不使用object.__new__(self),而使用self.__new__(self)
# 虽然上述效果一样,但使用self.__new__(self),查找属性时会顺着mro列表查找最后找到object中,
# 如果直接使用object.__new__(self),会直接跳过中间的父类们,拿不到中间对属性的操作
# Mymeta也是一个对象,那么Mymeta之所以可以调用,一定是在元类type中有一个__call__方法
class Mymeta(type):
def __new__(cls, *args, **kwargs):
"""自定义生成新对象的格式"""
obj = type.__new__(cls, *args, **kwargs) # 必须按照这种方式传值
print(obj.__dict__)
return obj # 得到对象必须返回, 才能执行后面的__init__
def __init__(cls, class_name, class_bases, class_namespace):
print("init.....")
class Student(metaclass=Mymeta): # 相当于Student=Mymeta("Student", (object, ), {...})
def __init__(self, name, age):
self.name = name
self.age = age
def study(self):
print("%s is studying"%self.name)
print(type(Mymeta))
# <class 'type'>
# 产生Student的过程就是调用Mymeta,那调用Mymeta一定是执行了type中的__call__方法
# class type:
# def __call__(self, *args, **kwargs): #self=<class '__main__.Mymeta'>
# obj=self.__new__(self,*args,**kwargs) # 产生Mymeta的一个对象
# self.__init__(obj,*args,**kwargs)
# return obj
class Mymeta(type): def __new__(cls, class_name:str, class_bases:tuple, class_namespace:dict): """自定义生成新对象的格式""" # 把自定义类的数据属性变大写 new_class_namespace = {} for k, v in class_namespace.items(): #type: str if not callable(v) and not k.startswith("__"): new_class_namespace[k.upper()] = v else: new_class_namespace[k] = v obj = type.__new__(cls, class_name, class_bases, new_class_namespace) # 必须按照这种方式传值 return obj # 得到对象必须返回, 才能执行后面的__init__ def __init__(cls, class_name, class_bases, class_namespace): pass class Student(metaclass=Mymeta): # 相当于Student=Mymeta("Student", (object, ), {...}) school = "No.4 school" classnoom = "" def __init__(self, name, age): self.name = name self.age = age def study(self): print("%s is studying"%self.name) print(Student.__dict__) # 结果: """ {'__module__': '__main__', 'SCHOOL': 'No.4 school', 'CLASSNOOM': '', '__init__': <function Student.__init__ at 0x00000288A1082840>, 'study': <function Student.study at 0x00000288A10828C8>, '__dict__': <attribute '__dict__' of 'Student' objects>, '__weakref__': <attribute '__weakref__' of 'Student' objects>, '__doc__': None} """ # 对象的使用也必须使用大写 stu1 = Student("sun", 23) print(stu1.SCHOOL) # 结果:No.4 school
# 要求: # 1. 自定义类实例化是必须以关键字传参(对__call__操作) # 2. key作为自定义类对象的属性,且所有属性变大写 class Mymeta(type): def __init__(cls, class_name, class_bases, class_namespace): pass def __call__(self, *args, **kwargs): if args: raise TypeError("必须使用关键字传值") obj = self.__new__(self) for k, v in kwargs.items(): obj.__dict__[k.upper()] = v return obj class Student(metaclass=Mymeta): # 相当于Student=Mymeta("Student", (object, ), {...}) def study(self): print("%s is studying"%self.name) stu1 = Student(name='sun', age=23) print(stu1.__dict__) print(stu1.NAME)
# 多次实例化的结果指向同一个实例,用于节省空间 # 如果从配置文件中读取配置进行实例化,在配置相同的情况下,就没有必要产生重复对象浪费内存了 # settings.py文件下的内容 PORT = 3306 HOST= "127.0.0.1" #第一种方法,在类中实现单例模式 import settings class Mysql: __instance = None def __init__(self, host, port): self.host = host self.ip = port @classmethod def singleton(cls): if not cls.__instance: cls.__instance = cls(settings.HOST, settings.PORT) return cls.__instance # 当直接调用类的方法singleton得到的对象是单例模式 obj1 = Mysql.singleton() obj2 = Mysql.singleton() # 但实例化得到的对象又是一个新的对象 obj3 = Mysql("127.0.0.1", 3306) # 第二种方法,使用装饰器实现单例 from functools import wraps import settings def singleton(cls): __instance = cls(settings.HOST, settings.PORT) @wraps(cls) def wrapper(*args, **kwargs): if args or kwargs: obj = cls(*args, **kwargs) return obj return __instance return wrapper @singleton class Mysql: def __init__(self, host, port): self.host = host self.ip = port # 当不传参数时可以实现单例,obj1和obj2的内存地址都是一样 obj1 = Mysql() obj2 = Mysql() # 当传入参数时,又是另一个新对象 obj3 = Mysql("127.0.0.1", 3306) # 第三种方法:使用元类 import settings class Mymeta(type): def __init__(cls, class_name, class_bases, class_namespace): # 事先从配置文件中取数据造一个Mysql对象 cls.__instance = cls(settings.HOST, settings.PORT) super().__init__(class_name, class_bases, class_namespace) def __call__(self, *args, **kwargs): # 当传入参数时重新创建一个对象 if args or kwargs: obj = self.__new__(self) self.__init__(obj, *args, **kwargs) return obj return self.__instance class Mysql: def __init__(self, host, port): self.host = host self.port = port # 当不传参数时可以实现单例,obj1和obj2的内存地址都是一样 obj1 = Mysql() obj2 = Mysql() # 当传入参数时,又是另一个新对象 obj3 = Mysql("127.0.0.1", 3306) # 第四种方式,使用导入模块的方式 # 导入模块,第一导入会执行模块内的代码生成名称空间 # 之后再次导入,都会指向这个名称空间 # 创建一个单例模块 # singleton.py模块 import settings class Mysql: def __init__(self, host, port): self.host = host self.port = port instance=MySQL(settings.HOST,settings.PORT) # 多次导入 def f1(): from singleton import instance print(instance) def f2(): from singleton import instance,Mysql print(instance) obj=MySQL('127.0.0.1',3306) print(obj) f1() f2() # 结果:两个instance的内存地址一样,和obj的地址不一样

浙公网安备 33010602011771号