13_Python的面向对象编程-类class,对象object,实例instance
1.面向对象概述
1.类是用来描述对象的工具,把拥有相同属性和行为的对象分为一组
2.对象是由类实例化出来的一个具体的对象
属性: 对象拥有的名词,用变量表示
行为: 对象拥有的动作,用方法表示
3.面向对象是把一切看成对象,对象和对象之间用方法建立关联关系
4.面向过程是一件事怎么一步一步实现,面向对象是一件事情谁去实现
5.可以用类创建一个或多个此类的对象,类中的变量和方法能被此类所创建的所有对象所共同拥有
6.面向对象编程语言的特征封装,继承,多态
7.类和对象流程图: https://www.processon.com/view/link/5ee0e5b5f346fb1ae558f314
类 对象 属性 方法 车 BYD E6(京A.88888) 行驶 狗 小京巴 户籍号:00001 喝水
2.类的概述
1.类的创建语法
class 类名(继承列表): """类文档字符串""" 实例方法(methd) 的定义 类变量(classVariable) 的定义 类方法(@classmethod) 的定义 静态方法(@staticmethod) 的定义
2.类名必须是标识符,即由数字字母下划线组成且不能以数字开头和不能是关键字,建议用大驼峰命名法,如: WebServer
3.类名实质上就是变量,它绑定一个类实例,属性是这类事务具有什么样的特征,方法是这类事务具有什么样的行为
4.在Python2中不指定继承object会定义经典类,Python3中无论是否制定继承object,都会创建新式类
# Python2中不指定继承object会创建经典类 class A: pass # Python2中定义新式类 class B(object): pass # Python3中无论是否制定继承object,都会创建新式类 class C: pass
5.使用help(__builtins__)可以查看Python中所有的类的继承派生关系
# 查看类的继承派生关系 help(__builtins__)
3.实例与实例化概述
实例概述:
1.实例就是有类诞生出的一个对象,实例有自己的作用域或名字空间,可以为该实例添加实例变量
2.同一个类实例化出的对象是不同的个体,实例可以调用类的方法和访问类中的类变量
实例化概述:
1.实例化就是类创建一个对象,并返回此实例对象的引用关系的过程
2.语法: obj = 类名([创建传参列表]) # 创建这个类的实例化对象,并返回此实例对象的引用关系 []里的形参可省略
class Dog: # 定义一个狗类 pass dog1 = Dog() # 用类来创建一个对象用dog1绑定 print(id(dog1)) # 打印dog1所在的内存地址 dog2 = Dog() # 创建第二个对象 用dog2绑定 print(id(dog2)) # 判断两只示例化出来的狗对象是不是同一条狗 print(dog1 is dog2) # False
4.类的组成成员: 字段,方法,静态属性
1.公有静态字段: 属于类在内存中只保存一份,类和实例都可以访问,类内部和派生类中也可以访问
2.私有静态字段: 属于类在内存中只保存一份,仅类内部可以访问
3.公有普通字段: 属于对象在每个对象中都要保存一份,实例可以访问,类内部和派生类中也可以访问
4.私有普通字段: 属于对象在每个对象中都要保存一份,仅类内部可以访问
5.实例方法: 属于类在内存中只保存一份,由对象调用,至少一个self参数,实例可以访问,类内部和派生类中也可以访问
6.私有方法: 属于类在内存中只保存一份,仅类内部可以访问,命名以双下划线开头,没有参数
7.类方法: 属于类在内存中只保存一份,由类调用,至少一个cls参数,执行类方法时,自动将调用该方法的类赋值给cls
8.静态方法: 属于类在内存中只保存一份,由类调用,无默认参数
9.静态属性: 属于类在内存中只保存一份,实例可以访问,类内部和派生类中也可以访问
class ClassMember: """此示例阐述类的组成成员""" company_name = "x公司" # 公有静态字段(公有静态变量,类变量),类和实例都可以访问,类内部和派生类中也可以访问 __iphone = "135xxxxxxxx" # 私有静态字段(私有静态变量,私有类变量),仅类内部可以访问 def __init__(self, name, age): # 初始化方法 self.name = name # 公有普通字段(实例属性),实例可以访问,类内部可以访问,派生类中可以访问 self.__age = age # 私有普通字段(私有实例属性),仅类内部可以访问 def func1(self): # 实例方法,至少有一个self参数,实例可以访问,类内部可以访问,派生类中可以访问 pass def __func(self): # 私有方法,仅类内部可以访问 print(666) @classmethod # 类方法 def class_func(cls): """定义类方法,至少有一个cls参数""" print("类方法") @staticmethod # 静态方法 def static_func(): """定义静态方法,无默认参数""" print("静态方法") @property # 静态属性,将方法伪装成属性来使用 def prop(self): pass
5.公有静态字段与私有静态字段
1.公有静态字段概述(公有静态变量,公有类变量)
1.类变量类可以通过类名.来访问,类内部和派生类中也可以访问
2.类变量是类的属性,此属性属于类,不属于此类创建的实例
3.类变量可以通过此类实例化对象访问,可以通过实例的 '__class__'属性间接修改
class Human: total_count = 0 # 类变量, 用于记录对象的个数 print(Human.total_count) h1 = Human() print(h1.total_count) # 0 # 不会出错 Human.total_count = 1 # 修改类变量 h1.total_count = 2 # 添加了自己的实例属性total_count h1.__class__.total_count = 3 # 间接修改类变量
class Human: total_count = 0 # 类变量,用于记录对象的个数 def __init__(self, name): self.name = name self.__class__.total_count += 1 # 人数加1 print(name, "对象创建") def __del__(self): self.__class__.total_count -= 1 # 总人数减1 print("当前对象的个数是:", Human.total_count) # 0 h1 = Human("张飞") h2 = Human("赵云") print("当前对象的个数是:", Human.total_count) # 2 del h2 # 或 h2 = None print("当前对象的个数是:", Human.total_count) # 1
class C: name = "公有静态字段" def func(self): print(C.name) class D(C): def show(self): print(C.name) C.name # 类访问 obj = C() obj.func() # 类内部可以访问 obj_son = D() obj_son.show() # 派生类中可以访问
2.私有静态字段(私有静态变量,私有类变量)
1.用户在创建类是可以在属性名前加双下划线来声明该属性只属于类所有,仅类内部可以访问
2.私有类变量不能被外界的实例对象直接访问,也不可以通过该类直接访问,不可在派生类中访问
3.私有类变量可以通过_类名__变量名来间接访问
class C: __name = "私有静态字段" def func(self): print(C.__name) class D(C): def show(self): print(C.__name) # C.__name # 不可在外部访问 obj = C() # obj.__name # 不可在外部访问 obj._C__name # 可以通过_类名__属性名来间接访问 obj.func() # 类内部可以访问 obj_son = D() obj_son.show() # 不可在派生类中访问
3.变量的赋值规则: 首次为变量赋值则创建此变量,再次为变量赋值则改变变量的绑定关系
4.在定义变量时如果不确定设置什么初始值,可以设置为None
5.删除变量del语句,语法: del 类名.变量名
6.类名.变量查找顺序: 先从本类空间找,如果找不到,再从父类找...
7.可以通过类的__dict__方法查看类的所有属性所在的字典
8.可以通过dir函数查看类的所有属性和方法
6.公有普通字段与私有普通字段
1.公有普通字段概述(公有实例属性,公有实例变量)
1.每个实例可以有自己的属性,实例属性的使用语法: 实例.属性名
2.实例属性: 对象可以访问,类内部和派生类中也可以访问
class C: def __init__(self): self.foo = "公有普通字段" def func(self): print(self.foo) # 类内部访问 class D(C): def show(self): print(self.foo) # 派生类中访问 obj = C() obj.foo # 通过对象访问 obj.func() # 类内部访问 obj_son = D() obj_son.show() # 派生类中访问
class Dog: def __init__(self): self.name = None self.color = None dog1 = Dog() dog1.kinds = "京巴" # 为dog1绑定的实例添加kinds属性 dog1.color = "白色" # 添加属性 print(dog1.kinds, dog1.color) # 访问属性 dog1.color = '黄色' # 修改dog1.color 的绑定关系 print(dog1.color)
2.私有普通字段概述(私有实例属性,私有实例变量)
1.每个实例可以有自己的私有属性,称为私有实例属性或私有实例变量,使用语法: 实例.类中的引用方法名
2.私有实例属性: 仅类内部可以访问,但可以通过_类名__属性名来间接访问
class C: def __init__(self): self.__foo = "私有字段" def func(self): print(self.foo) # 类内部访问 class D(C): def show(self): print(self.foo) # 派生类中访问 obj = C() obj.__foo # 通过对象访问会报错 obj.func() # 类内部可以访问 obj_son = D() obj_son.show() # 派生类中访问会报错
class Women: def __init__(self, name): self.name = name self.__age = 18 def secret(self): print("%s的年龄是%d" % (self.name, self.__age)) xiaomei = Women("小美") # print(xiaomei.__age) # 私有属性不能被外界的实例对象直接访问 xiaomei.secret() # 在对象内部的方法可以直接访问对象的私有属性 print(xiaomei._Women__age) # 私有属性可以通过_类名__属性名来间接访问 xiaomei.__dict__ # {'name': '小美', '_Women__age': 18} dir(xiaomei)[0:3] # ['_Women__age', '__class__', '__delattr__']
3.属性的赋值规则: 首次为属性赋值则创建此属性,再次为属性赋值则改变属性的绑定关系
4.在定义属性时如果不确定设置什么初始值,可以设置为None
5.删除属性del语句,语法: del 对象.属性名
class Student: pass stu = Student() stu.name = 'xiaozhang' # 创建属性 print(stu.name) del stu.name # 删除此属性 print(stu.name) # 属性错误,因为属性已经不存在了
6.对象.属性查找顺序: 先从对象空间找,如果找不到,再从类空间找,再找不到,再从父类找...
7.可以通过对象的__dict__方法查看对象所有属性所在的字典
8.可以通过dir函数查看对象的所有属性和方法
7.实例方法与私有方法
1.实例方法概述
1.实例方法的实质是函数,是定义在类内的函数,用于描述一个对象的行为,让此类型的全部对象都拥有相同的行为
2.实例方法至少有一个self参数代表调用这个方法的实例,声明该方法是实例方法
3.实例方法对象可以访问,类内部和派生类中也可以访问
4.实例方法的调用语法:
实例.实例方法名(调用参数)
类名.实例方法名(实例, 调用参数)
class C: def __init__(self): pass def add(self): print("this is add") class D(C): def show(self): print("this is show") def func(self): self.show() obj = D() obj.show() # 通过对象访问 obj.func() # 类内部访问 obj.add() # 派生类中访问 D.func(obj) # 类名调用
2.私有方法概述
1.用户在创建类是可以在方法名前加双下划线(__方法名)来声明该属性只属于类所有
2.私有方法不能被外界的实例对象直接访问
3.用户自定义的私有方法可以通过_类名__方法名来间接访问
class Women: def __init__(self, name): self.name = name self.__age = 18 def __secret(self): print("%s的年龄是%d" % (self.name, self.__age)) xiaomei = Women("小美") # xiaomei.__secret() # 私有方法不能被外界的实例对象直接访问 xiaomei._Women__secret() # 私有方法可以通过_类名__方法名来间接访问 xiaomei.__dict__ # {'name': '小美', '_Women__age': 18} dir(xiaomei)[0:3] # ['_Women__age', '_Women__secret', '__class__']
3.删除方法del语句,语法: del 类名.方法名
4.类名.方法查找顺序: 先从本类空间找,如果找不到,再从父类找...
5.可以通过对象的__dict__方法查看对象所有属性所在的字典
6.可以通过dir函数查看对象的所有属性和方法
8.类方法与静态方法和静态属性(@classmethod, @staticmethod, @property)
1.类方法 classmethod
类方法概述:
1.类方法是操作类的方法,类方法属于类,不属于该类创建的对象
2.类方法需要使用 @classmethod 装饰器定义,第一个参数用来绑定类,约定写为cls
3.类和对象实例都可以调用类方法,类方法不能访问此类创建的对象的属性
4.类方法不用对象命名空间中的内容,而是用到了类命名空间中的变量(静态属性),或者类方法,静态方法
class A: v = 0 # 类变量 @classmethod def get_v(cls): # 此方法是类方法 return cls.v a = A() a.get_v() # 0 A.get_v() # 0 a.__dict__ # {}
class Goods: __discount = 0.8 def __init__(self, price): self.__price = price @property def price(self): return self.__price * Goods.__discount @classmethod def change_discount(cls, num): cls.__discount = num # 商场的程序 apple = Goods(10) banana = Goods(15) print(apple.price,banana.price) Goods.change_discount(1) print(apple.price,banana.price)
2.静态方法 staticmethod
静态方法概述:
1.静态方法是定义在类的内部的函数,此函数作用域是类的内部
2.静态方法需要使用@staticmethod 装饰器定义,定义上与普通函数的定义相同,不需要传入self和cls
3.静态方法只能凭借该类和实例来调用,不能访问类变量和实例变量
4.如果一个类里面的方法既不需要用到self中的资源,也不用cls中的资源,就将这个方法定义成一个静态方法
class A: @staticmethod def myadd(a, b): # 此方法是静态方法 return a + b print(A.myadd(100, 200)) # 300 a = A() print(a.myadd(300, 400)) # 300
# 装饰登录功能 class Person: @staticmethod def login(): # 动词 动作 属于某一个对象 pass class Student(Person): pass class Manager(Person): pass class Course: pass class Classes: pass
3.静态属性 property
静态属性概述:
1.将方法伪装成一个静态属性,代码上没有什么提升只是更合理,静态属性本质就是实现了 get,set,delete 三种方法
2.静态属性需要使用 @property 装饰器定义,只和实例对象绑定既可以访问实例属性也可以访问类的属性
3.property的构造方法中有个四个参数
1.第一个参数是方法名,调用 对象.属性 时自动触发执行方法
2.第二个参数是方法名,调用 对象.属性 = XXX 时自动触发执行方法
3.第三个参数是方法名,调用 del 对象.属性 时自动触发执行方法
4.第四个参数是字符串,调用 对象.属性.__doc__ 时此参数是该属性的描述信息
4.@方法名.setter装饰器:
修改property装饰的属性时会被调用这个装饰器装饰的方法
除了self外还有一个参数是被修改的值
5.@方法名.deleter装饰器:
当要删除被property装饰的属性时会被调用这个装饰器的方法
实际不会删除被property装饰的方法,而是在执行'del 类名.属性'删除操作时执行这个装饰器的方法
6.定义property属性有两种方式: 在方法上应用装饰器和在类中定义值为property对象的类属性
# property属性定义方式: 类中定义值为property对象的类属性 class Foo: def get_bar(self): return 'Tom' # 必须两个参数 def set_bar(self, value): return return 'set value' + value def del_bar(self): return 'Tom' BAR = property(get_bar, set_bar, del_bar, 'description') obj = Foo() obj.BAR # 自动调用第一个参数中定义的方法: get_bar obj.BAR = "Cat" # 自动调用第二个参数中定义的方法: set_bar方法,并将"Cat"当作参数传入 del Foo.BAR # 自动调用第三个参数中定义的方法: del_bar方法 obj.BAE.__doc__ # 自动获取第四个参数中设置的值: description
# property属性定义方式: 在方法上应用装饰器 class Person: def __init__(self, name, age): self.name = name if type(age) is int: self.__age = age else: print('你输入的年龄的类型有误,请输入数字') @property def age(self): return self.__age @age.setter def age(self, a1): '''判断,你修改的年龄必须是数字''' if type(a1) is int: self.__age = a1 else: print('你输入的年龄的类型有误,请输入数字') @age.deleter def age(self): del self.__age p1 = Person('帅哥', 20) print(p1.age) # 20 print(p1.__dict__) # {'name': '帅哥', '_Person__age': 20} p1.age = 23 print(p1.age) # 23 del p1.age
# property属性定义方式: 在方法上应用装饰器 class Money(object): def __init__(self): self.__money = 0 # 使用装饰器对money进行装饰,那么会自动添加一个叫money的属性,当调用获取money的值时调用装饰的方法 @property def money(self): return self.__money # 使用装饰器对money进行装饰,当对money设置值时,调用装饰的方法 @money.setter def money(self, value): if isinstance(value, int): self.__money = value else: print("error:不是整型数字") a = Money() a.money = 100 print(a.money) # property属性定义方式: 在方法上应用装饰器 class Money(object): def __init__(self): self.__money = 0 def getMoney(self): return self.__money def setMoney(self, value): if isinstance(value, int): self.__money = value else: print("error:不是整型数字") # 定义一个属性,当对这个money设置值时调用setMoney,当获取值时调用getMoney money = property(getMoney, setMoney) a = Money() a.money = 100 # 调用setMoney方法 print(a.money) # 调用getMoney方法
# property属性定义方式: 在方法上应用装饰器,实现类型检测功能 class People: def __init__(self, name): self.name = name # 实例化就触发property @property def name(self): # return self.name # 无限递归 print('get------>') return self.DouNiWan @name.setter def name(self, value): print('set------>') if not isinstance(value, str): raise TypeError('必须是字符串类型') self.DouNiWan = value @name.deleter def name(self): print('delete------>') del self.DouNiWan p1 = People('echo') # self.name实际是存放到self.DouNiWan里 p1.name = 1
# property属性定义方式: 在方法上应用装饰器,验证@deleter不会删除被property装饰的方法 class A: def __init__(self, name): self.__name = name @property def name(self): return self.__name @name.setter def name(self, new_name): if type(new_name) is str: self.__name = new_name @name.deleter def name(self): def self.__name a = A("Ccoc") print(a.name) a.name = "echo" del a.name # 看似是删除了name属性,实际是删除了__name私有属性 # print(a)
# property属性定义方式: 在方法上应用装饰器,让调用看起来更加合理 class Pager: def __init__(self, current_page): # 用户当前请求的页码(第一页,第二页...) self.current_page = current_page # 每页默认显示10条数据 self.per_items = 10 @property def start(self): val = (self.current_page - 1) * self.per_items return val @property def end(self): val = self.current_page * self.per_items return val p = Pager(1) p.start # 就是起始值,即: m p.end # 就是结束值,即: n
# property属性定义方式: 在方法上应用装饰器,取代getater和seatter以及delattr方法 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() obj.price # 获取商品价格 obj.price = 200 # 修改商品原价 del obj.price # 删除商品原价
# property属性定义方式: 使用类属性的方式创建property属性改写示例7 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, '价格属性描述...') obj = Goods() obj.PRICE # 获取商品价格 obj.PRICE = 200 # 修改商品原价 del obj.PRICE # 删除商品原价
4.静态方法,类方法,静态属性综合示例
class Room(object): tag = 1 def __init__(self, wight, length): self.wight = wight self.length = length @property def car_area(self): return self.length * self.wight + self.tag @classmethod def tell_info(cls): print(cls) print("-->", cls.tag) @staticmethod def run(a, b): print("%s %s 正在跑步" % (a, b)) # 静态属性: 将类的函数属性@property装饰后,是对象以数据属性的方式调用 # 只和实例对象绑定-->即可以访问实例属性也可以访问类的属性 p1 = Room(10, 20) print("类属性", p1.tag) print("实例对象调用静态属性", p1.car_area) # 类方法: 将类的函数属性@classmethod装饰后,不用创建实例对象,调用类的函数属性 # 只和类绑定-->可以访问类属性,不能访问实例属性 Room.tell_info() # 静态方法: 将类的函数属性@staticmethod装饰后,不创建和创建实例对象,都可以调用类的函数属性 # 即不和类绑定也不和实例对象绑定-->不能访问类属性,也不能访问实例属性,是类的工具包 Room.run("co1", "co2") p2 = Room(20, 20) p2.run("co3", "co4")
5.Django框架中应用了property属性
class WSGIRequest(http.HttpRequest): def __init__(self, environ): script_name = get_script_name(environ) path_info = get_path_info(environ) if not path_info: # Sometimes PATH_INFO exists, but is empty (e.g. accessing # the SCRIPT_NAME URL without a trailing slash). We really need to # operate as if they'd requested '/'. Not amazingly nice to force # the path like this, but should be harmless. path_info = '/' self.environ = environ self.path_info = path_info self.path = '%s/%s' % (script_name.rstrip('/'), path_info.lstrip('/')) self.META = environ self.META['PATH_INFO'] = path_info self.META['SCRIPT_NAME'] = script_name self.method = environ['REQUEST_METHOD'].upper() _, content_params = cgi.parse_header(environ.get('CONTENT_TYPE', '')) if 'charset' in content_params: try: codecs.lookup(content_params['charset']) except LookupError: pass else: self.encoding = content_params['charset'] self._post_parse_error = False try: content_length = int(environ.get('CONTENT_LENGTH')) except (ValueError, TypeError): content_length = 0 self._stream = LimitedStream(self.environ['wsgi.input'], content_length) self._read_started = False self.resolver_match = None def _get_scheme(self): return self.environ.get('wsgi.url_scheme') def _get_request(self): warnings.warn('`request.REQUEST` is deprecated, use `request.GET` or ' '`request.POST` instead.', RemovedInDjango19Warning, 2) if not hasattr(self, '_request'): self._request = datastructures.MergeDict(self.POST, self.GET) return self._request @cached_property def GET(self): # The WSGI spec says 'QUERY_STRING' may be absent. raw_query_string = get_bytes_from_wsgi(self.environ, 'QUERY_STRING', '') return http.QueryDict(raw_query_string, encoding=self._encoding) # ############### 看这里看这里 ############### def _get_post(self): if not hasattr(self, '_post'): self._load_post_and_files() return self._post # ############### 看这里看这里 ############### def _set_post(self, post): self._post = post @cached_property def COOKIES(self): raw_cookie = get_str_from_wsgi(self.environ, 'HTTP_COOKIE', '') return http.parse_cookie(raw_cookie) def _get_files(self): if not hasattr(self, '_files'): self._load_post_and_files() return self._files # ############### 看这里看这里 ############### POST = property(_get_post, _set_post) FILES = property(_get_files) REQUEST = property(_get_request)
9.作用于类的内置函数(isinstance issubclass object super vars type)
1.isinstance函数
isinstance(obj, class_or_tuple):
返回这个对象是否是某个类的对象,或者某些类中的一个类的对象,如果是则返回True,否则返回False
class Foo(object): pass obj = Foo() isinstance(obj, Foo) # True isinstance(1, int) # True isinstance("", (int, str, list)) # # True
2.issubclass函数
issubclass(cls, class_or_tuple):
判断一个类是否是继承自其它的类,如果此类cls是class或tuple中的一个派生子类则返回True,否则返回False
class A: pass class B(A): pass class C(B): pass class D(B): pass issubclass(B, A) # True issubclass(C, B) # True issubclass(D, C) # False issubclass(C, (int, str)) # False
3.object函数
object函数概述:
1.object类是Python中所有类的基类,如果定义一个类时没有指定继承哪个类,则默认继承object类
2.object没有定义__dict__所以不能对object类实例对象尝试设置属性
3.object函数返回一个新的无特征对象,obj = object()
class A: pass # 默认继承object类 print(issubclass(A,object)) # True # object类定义了所有类的一些公共方法 print(dir(object)[0:5]) # ['__class__', '__delattr__', '__dir__', '__doc__', '__eq__'] # object函数返回一个新的无特征对象 obj = object() type(obj) # object print(obj) # <object object at 0x0000000003E89EF0>
# 定义一个类A class A: pass a = A() a.name = 'li' # 能设置属性 b = object() b.name = 'wang' # 不能设置属性 """执行结果 AttributeError Traceback (most recent call last) <ipython-input-23-fae4aa8bc37b> in <module> 7 8 b = object() ----> 9 b.name = 'wang' # 不能设置属性 AttributeError: 'object' object has no attribute 'name' """
4.super函数
super函数概述:
1.返回超类的实例,用超类实例来调用其自身的方法,super()就是使用super类创建出来的对象
2.当子类实现了__init__方法后,父类的__init__方法将被覆盖,不再会主动调用父类的__init__方法
3.当子类实现了__init__方法后,会引起父类的属性得不到初始化,需要父类初始化属性必须要在子类中调用super函数
4.super(type, obj): 返回绑定超类的实例(要求obj必须为type类型的实例)
5.super(): 返回绑定超类的实例,等同于super(__class__, 实例的第一个参数), 且必须在方法内调用
# 此示例示意用super函数访问父类的覆盖方法 class A: def work(self): print("A类的work方法被调用") class B(A): def work(self): print("B类的work方法被调用") def doworks(self): # self.work() # 调用B类的方法 super(B, self).work() # 调用超类的方法 super().work() # Python推荐的调用超类的调用方法 # super(__class__, self).work() # 一样会调用超类的方法 b = B() b.work() # B类的work方法被调用 print("-----以下用b调用覆盖版本的方法----") # A.work(b) # A类的work方法被调用 super(B, b).work() b.doworks()
# 此示例示意显式调用基类的初始化方法 class Human: def __init__(self, n, a): self.name = n self.age = a print("Human类的 __init__被调用") def show_info(self): print("姓名:", self.name) print("年龄:", self.age) class Student(Human): """学生类""" def __init__(self, n, a, s=0): super().__init__(n, a) # 显式调用基类的初始化方法 self.score = s print("Student类的 __init__被调用") def show_info(self): super().show_info() print("成绩:", self.score) s1 = Student('coco', 20) s1.show_info()
5.vars函数
vars() 函数返回对象object的属性和属性值的字典对象
print(vars()) """ {'__builtins__': <module '__builtin__' (built-in)>, '__name__': '__main__', '__doc__': None, '__package__': None} """
class Runoob: a = 1 print(vars(Runoob)) # {'a': 1, '__module__': '__main__', '__doc__': None} runoob = Runoob() print(vars(runoob)) # {}
6.type函数(type元类)
type元类概述:
1.type元类是获取该对象从属于的类
2.Python原则是: 一切皆对象,其实类也可以理解为'对象',而type元类又称作构建类
3.Python中大多数内置的类(包括object)以及自己定义的类,都是由type元类创造的
4.而type类与object类之间的关系比较独特:
1.object是type类的实例,而type类是object类的子类
2.这种关系比较神奇无法使用python的代码表述,因为定义其中一个之前另一个必须存在
# type(obj)返回对象的类 class A: pass a = A() type(a) # __main__.A
10.属性管理内置函数(反射函数)-getattr, hasattr, setattr, delattr
反射概述: 从某个指定的命名空间中,用字符串数据类型的变量名来获取变量的值
getattr(obj, name[, default]):
从一个对象得到对象的属性;getattr(x, 'y') 等同于x.y
当属性不存在时,如果给出default参数,则返回default,如果没有给出default 则产生一个AttributeError错误
hasattr(obj, name):
用给定的name返回对象obj是否有此属性,此种做法可以避免在getattr(obj, name)时引发错误
setattr(obj, name, value):
给对象obj的名为name的属性设置相应的值value, setattr(x, 'y', v) 等同于 x.y = v
delattr(obj, name):
删除对象obj中的name属性, delattr(x, 'y') 等同于 del x.y
# 对类的反射-拿静态属性,类方法,静态方法 class Student: ROLE = 'STUDENT' @classmethod def check_course(cls): print('查看课程了') @staticmethod def login(): print('登录') # 反射查看属性 print(Student.ROLE) # STUDENT print(getattr(Student, 'ROLE')) # STUDENT # 反射调用方法 getattr(Student, 'check_course')() # 查看课程了 getattr(Student, 'login')() # 登录 num = input('>>>') # login if hasattr(Student, num): getattr(Student, num)() # 登录
# 对实例化对象的反射-拿属性,方法 class Foo: f = '类的静态变量' def __init__(self, name, age): self.name = name self.age = age def say_hi(self): print('hi,%s' % self.name) obj = Foo('egon', 73) # 检测是否含有某属性 print(hasattr(obj, 'name')) print(hasattr(obj, 'say_hi')) # 获取属性 n = getattr(obj, 'name') print(n) func = getattr(obj, 'say_hi') func() print(getattr(obj, 'aaaaaaaa', '不存在')) # 报错 # 设置属性 setattr(obj, 'sb', True) setattr(obj, 'show_name', lambda self: self.name + 'sb') print(obj.__dict__) print(obj.show_name(obj)) # 删除属性 delattr(obj, 'age') delattr(obj, 'show_name') delattr(obj, 'show_name111') # 不存在,则报错 print(obj.__dict__)
# 反射当前模块成员-拿自己的变量名 import sys def s1(): print('s1') def s2(): print('s2') this_module = sys.modules[__name__] hasattr(this_module, 's1') getattr(this_module, 's2')
# 导入其他模块,利用反射-拿模块中的方法 """程序目录 module_test.py index.py """ # module_test.py中的代码 def test(): print('from the test') # index.py中的代码 import module_test as obj # obj.test() print(hasattr(obj, 'test')) getattr(obj, 'test')()
# 反射的应用 class User: def login(self): print('欢迎来到登录页面') def register(self): print('欢迎来到注册页面') def save(self): print('欢迎来到存储页面') user = User() while 1: choose = input('>>>').strip() if hasattr(user, choose): func = getattr(user, choose) func() else: print('输入错误...')
# 四个反射函数综合运用 class BlackMedium: feature = 'Ugly' def __init__(self, name, addr): self.name = name self.addr = addr def sell_house(self): print('%s 卖房子' %self.name) def rent_house(self): print('%s 出租' %self.name) b1 = BlackMedium('桃园', 'xx区xx路') # 检测是否含有某属性 print(hasattr(b1, 'name')) print(hasattr(b1, 'sell_house')) # 获取属性 n = getattr(b1, 'name') print(n) func = getattr(b1, 'rent_house') func() # getattr(b1, 'a') # 报错 print(getattr(b1, 'a', '不存在')) # 设置属性 setattr(b1, 'sb', True) setattr(b1, 'show_name', lambda self: '景秀' + self.name) print(b1.__dict__) print(b1.show_name(b1)) # 删除属性 delattr(b1, 'addr') delattr(b1, 'show_name') # delattr(b1, 'show_name111') # 不存在则报错 print(b1.__dict__)
11.函数与方法的区别
1.函数的是显式传递数据的,是操作参数传入的数据,与对象无关
2.方法中的数据则是隐式传递的,方法可以操作类内部的数据,和对象有关联
3.打印对象名称可以确定对象是函数还是方法
def func(): pass print(func) # <function func at 0x10f65c170> class A: def func(self): pass print(A.func) # <function A.func at 0x10f67c0e0> obj = A() print(obj.func) # <bound method A.func of <__main__.A object at 0x10f692250>>
4.模块验证对象是函数还是方法
from types import FunctionType from types import MethodType def func(): pass class A: def func(self): pass obj = A() print(isinstance(func, FunctionType)) # True print(isinstance(A.func, FunctionType)) # True print(isinstance(obj.func, FunctionType)) # False print(isinstance(obj.func, MethodType)) # True
5.静态方法其实是函数
from types import FunctionType from types import MethodType class A: def func(self): pass @classmethod def func1(cls): pass @staticmethod def func2(): pass obj = A() # 静态方法其实是函数 print(isinstance(A.func2, FunctionType)) # True print(isinstance(obj.func2, FunctionType)) # True print(isinstance(obj.func1, FunctionType)) # False
12.双下属性
1.类的文档字符串属性 __doc__
__doc__属性: 是类的描述信息,无法被子类继承
class Demo: """测试类""" pass Demo.__doc__ # '测试类'
class Foo: """描述信息""" pass class Bar(Foo): pass print(Bar.__doc__) # 该属性无法继承给子类
2.类的字典属性 __dict__
__dict__属性: 可以查询类中的所有属性和方法
class A: a = 1 def add(): pass A.__dict__ """执行结果 mappingproxy({'__module__': '__main__', 'a': 1, 'add': <function __main__.A.add()>, '__dict__': <attribute '__dict__' of 'A' objects>, '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None}) """
3.类的名称属性 __name__
class Demo: """测试类""" pass Demo.__name__ # 'Demo'
4.类的记录基类属性 __base__, __bases__
__base__属性: 用来记录此类的基类,返回一个字符串
__bases__属性: 用来记录此类的基类,返回一个元祖
class A: pass class B(A): pass B.__base__ # __main__.A B.__bases__ # (__main__.A,) A.__base__ # object A.__bases__ # (object,) type.__base__ # object str.__bases__ # (object,)
5.类的限定实例化对象的属性 __slots__
__slots__属性概述
1.是一个类变量,变量值可以是列表,元祖,或者可迭代对象,也可以是一个字符串(意味着所有实例只有一个固定的属性)
2.使用点来访问属性本质就是在访问类或者对象的__dict__属性字典(类的字典是共享的,而每个实例的是独立的)
3.字典会占用大量内存,如果类的属性很少但却有很多实例,为了节省内存可以使用__slots__取代实例的__dict__
4.含有 __slots__ 属性的类所创建的对象没有__dict__字典,统一归__slots__管理,有利于节省内存
class Student: # 限定此的类创建的对象只能有name和age两个属性 __slots__ = ['name', 'age'] def __init__(self, n, a): self.name = n self.age = a s1 = Student("小张", 20) # s1.Age = 21 # 此时是错写了age为Age, 会报错 # print(s1.__dict__) # 出错,因为没有__dict__字典
class Foo2: """文档描述""" # __slost__取代了类的__dict__使实例化除的属性没有__dict__方法 __slots__ = ["name", "age"] class Foo3(Foo2): def __del__(self): print("回收实例时触发") def __call__(self, *args, **kwargs): print("__call__方法让实例对象可以加()执行") f3 = Foo2 print("f3", f3.__slots__) # __doc__记录类的文档描述信息 print(Foo2.__doc__) # 文档描述 # __doc__无法被子类继承,因为子类一旦定义就有属于自己的__doc__ print(Foo3.__doc__) # None print(Foo3.__dict__) # __module__查看f3来自于哪个模块 print(f3.__module__) # __main__ # __class__查看f3来自于哪个类 print(f3.__class__) # <class 'type'> f3 = Foo3() # <class 'type'> f3() # __call__方法让实例对象可以加()执行 # __del__析构函数,会在回收实例时触发 del f3 # 回收实例时触发
6.实例的预置属性 __dict__, __class__
__dict__属性: 用于绑定一个存储此实例自身变量的字典
__class__ 属性: 用于绑定创建此实例的类,表示当前操作的对象的类是什么
class Dog: pass dog1 = Dog() print(dog1.__dict__) # {} dog1.color = "白色" print(dog1.__dict__) # {'color': '白色'} print(dog1.__class__) # <class '__main__.Dog'>
7.实例的隶属属性 __module__
__module__属性: 表示当前操作的对象在那个模块
# 模块 My_Module 中的代码 class C: def __init__(self): self.name = "Cat" # 主模块中的代码 from My_Module import C obj = C() # 输出obj对象所在的模块 print(obj.__module__) # My_Module # 即输出obj对象所在的类 print(obj.__class__) # My_Module.C
13.双下方法
1.构造方法 __new__
构造方式概述:
1.过程: 开辟一个属于对象的空间,把对象的空间传给self后执行init,最后将这个对象的空间返回给调用者
2.new方法在实例化之后,__init__ 之前先执行new来创建一块空间
3.当创建的类没有new方法时,会调用object的new方法
class Single: def __new__(cls, *args, **kwargs): # print('在new方法里') obj = object.__new__(cls) print('在new方法里', obj) return obj def __init__(self): print('在init方法里', self) # 实例化时,__new__构造方法先调用,接着__init__初始化方法调用 obj = Single() """执行结果 在new方法里 <__main__.Single object at 0x000000000525EDD8> 在init方法里 <__main__.Single object at 0x000000000525EDD8> """
2.初始化方法 __init__
初始化方法概述:
1.初始化方法名必须为 __init__ 不可改变,是对新创建的对象添加属性等必须的资源
2.初始化方法会在构造函数创建实例后自动调用, 且将实例自身通过第一个参数 self 传入 __init__ 方法
3.构造函数的实参将通过 __init__ 方法的参数列表传到 __init__ 方法中
4.初始化方法内如果需要 return 语句返回则必须返回 None
5.语法形式:
class 类名:
def __init__(self[, 参数列表]): # []代表其中内容可省略
语句块
# 此示例示意初始化方法的定义方法和调用过程 class Car: """小汽车类""" def __init__(self, c, b, m): self.color = c # 颜色 self.brand = b # 品牌 self.model = m # 型号 self.wheel = 4 print("__init__方法被调用") def run(self, speed): print(self.color, '的', self.brand, self.model, '正在以', speed, '公里/小时的速度行驶') def change_color(self, c): self.color = c a4 = Car('红色', '奥迪', 'A4') a4.run(199) a4.change_color("白色") a4.run(280) x5 = Car('蓝色', '宝马', 'x5') x5.run(188)
3.析构方法 __del__
析构方法概述:
1.在对象被销毁之前被调用,主要负责清理对象所占用的资源
2.Python建议尽可能少的在析构方法内做事情,因为销毁时间难以确定
3.对象借用了操作系统的资源还要通过析构方法归还资源,如文件资源,网络资源
4.语法形式:
class 类名:
def __del__(self):
语句块
class FileManage: """定义一个文件管理员类""" def __init__(self, filename='a.txt'): self.file = open(filename, 'w') def writeline(self, string): self.file.write(string) self.file.write('\n') def __del__(self): """析构方法会在对象销毁前自动调用""" self.file.close() print("文件已关闭") fm = FileManage() fm.writeline("hello world") fm.writeline("这是中文写的第二行") del fm # 文件已关闭 # 死循环永远不退出,del不调用一直不关闭文件 while True: pass print("程序结束")
4.调用方法 __call__
在创建类型的时候定义了__call__方法,这个对象就类似函数一样是可调用的,相当于 对象()
在写类的装饰器时会用到__call__方法
class A: def __call__(self, *args, **kwargs): print('执行call方法了') def call(self): print('执行call方法了') class B: def __init__(self, cls): print('在实例化A之前做一些事情') self.a = cls() self.a() print('在实例化A之后做一些事情') a = A() # 对象() 相当于调用__call__方法 a() # 执行call方法了 a.call() # 执行call方法了 # 类名()()相当于先实例化得到一个对象,再对对象(),和上面的结果一样,相当于调用__call__方法 A()() # 执行call方法了 B(A) """B(A)执行结果 在实例化A之前做一些事情 执行call方法了 在实例化A之后做一些事情 Out[11]: <__main__.B at 0x550a9b0> # 这行是调用B类的基类object的__call__方法执行的结果 """
5.item系列方法(__getitem__, __setitem__, __delitem__)
item方法概述:
1.item系列方法和对象使用[]访问值有联系,用于索引切片操作,常用语字典和列表对象
2.Python3中的切片方法不再是Python2中的__getslice__(), __setslice__()和__delslice__()
3.Python3中借助slice类把分片整合到了__getitem__(),__setitem__()和 __delitem__()中
obj = {'k':'v'} # 字典的对象 print(obj) # {'k': 'v'} print(obj['k']) # v class B: def __getitem__(self, item): return getattr(self, item) def __setitem__(self, key, value): setattr(self, key, value*2) def __delitem__(self, key): delattr(self, key) b = B() # b.k2 = 'v2' # print(b.k2) # __setitem__ b['k1'] = 'v1' # __getitem__ print(b['k1']) # v1v1 # __delitem__ del b['k1'] # print(b['k1']) # 会报错
# 字典的对象 class Foo: def __init__(self, name): self.name = name def __getitem__(self, item): print(self.__dict__[item]) def __setitem__(self, key, value): self.__dict__[key] = value def __delitem__(self, key): print('del obj[key]时,我执行') self.__dict__.pop(key) def __delattr__(self, item): print('del obj.key时,我执行') self.__dict__.pop(item) f1 = Foo('sb') f1['age'] = 18 f1['age1'] = 19 del f1.age1 # del obj.key时执行 del f1['age'] # del obj[key]时执行 f1['name'] = 'coco' print(f1.__dict__) # {'name': 'coco'}
# 字典的对象 class Foo(): def __getattr__(self, item): print("执行了getattr查找%s" % item) # 无论是否找到属性都会执行getattribute def __getattribute__(self, item): print("执行了getattribute查找%s" % item) raise AttributeError("抛出异常") f1 = Foo() # isinstance(obj, cls)判断对象obj是否是类cls实例化出来的对象 print(isinstance(f1, Foo)) # True f1.x """f1.x执行结果 执行了 __getattribute__ 查找x 执行了 __getattr__ 查找x """ class Bar(): # 只针对字典方式操作触发 def __getitem__(self, item): print("%s getitem" % item) return self.__dict__[item] # 只针对字典方式操作触发 def __setitem__(self, key, value): print("%s:%s setitem" % (key, value)) self.__dict__[key] = value # 只针对字典方式操作触发 def __delitem__(self, key): print("%s delitem" % key) self.__dict__.pop(key) # issubclass(sub, super)判断子类sub是否是由父类super继承来的派生类 print(issubclass(Bar, Foo)) # False f2 = Bar() f2["name"] = "coco" # name:coco setitem print(f2["name"]) """print(f2["name"])执行结果 name getitem coco """ f2["age"] = "18" # age:18 setitem print(f2.__dict__) # {'name': 'coco', 'age': '18'} del f2["age"] # age delitem print(f2.__dict__) # {'name': 'coco'}
# 列表的对象 class Foo(object): def __getitem__(self, index): if isinstance(index, slice): print("Get slice---------> start: %s, stop: %s, step: %s." \ % (str(index.start), str(index.stop), str(index.step))) def __setitem__(self, index, value): if isinstance(index, slice): print("Set slice---------> start: %s, stop: %s, step: %s." \ % (str(index.start), str(index.stop), str(index.step))) print("\tThe value is:", value) def __delitem__(self, index): if isinstance(index, slice): print("Delete slice------> start: %s, stop: %s, step: %s." \ % (str(index.start), str(index.stop), str(index.step))) if __name__ == "__main__": obj = Foo() obj[-1:10] # Get slice---------> start: -1, stop: 10, step: None. obj[-1:10:1] = [2, 3, 4, 5] """obj[-1:10:1] = [2, 3, 4, 5]执行结果 Set slice---------> start: -1, stop: 10, step: 1. The value is: [2, 3, 4, 5] """ del obj[-1:10:2] # Delete slice------> start: -1, stop: 10, step: 2.
# Python2中的分片(切片)操作 __getslice__ __setslice__ __delslice__ class Foo(object): def __getslice__(self, i, j): print('__getslice__', i, j) def __setslice__(self, i, j, sequence): print('__setslice__', i, j) def __delslice__(self, i, j): print('__delslice__', i, j) obj = Foo() obj[-1:1] # 自动触发执行 __getslice__ obj[0:1] = [11, 22, 33, 44] # 自动触发执行 __setslice__ del obj[0:2] # 自动触发执行 __delslice__
6.__eq__ 方法
__eq__方法定义了类的等号(==)行为
class A: def __init__(self, name, age): self.name = name self.age = age def __eq__(self, other): if self.name == other.name and self.age == other.age: return True a = A('gogo', 83) aa = A('gogo', 83) aa2 = A('gogo', 83) aa3 = A('gogo', 83) aa4 = A('gogo', 83) print(a, aa) # <__main__.A object at 0x0000000004F94FD0> <__main__.A object at 0x0000000004F94748> # ==这个语法 是完全和__eq__ 相同 print(aa3 == aa == aa4) # True
7.__str__ 方法和 __repr__ 方法
str函数和repr函数概述:
str函数返回对象的描述信息print函数输出使用
str函数或者print函数 实际调用--->obj.__str__(), print("%s", 变量名)
repr函数返回一个能代表次对象的字符串表达式(这个字符串是给Python解释器识别用的),通常用于远程通信数据校验
repr函数用于远程通信数据校验示例:eval(repr(obj)) == obj
repr函数或者交互式解释器 实际调用--->obj.__repr__(), print("%r", 变量名)
__str__ 和 __repr__ 方法概述:
如果__str__没有被定义,那么原本调用__str__方法的语法就会使用__repr__来代替输出
__str__ 和 __repr__ 方法的返回值必须是字符串,否则抛出异常
查找顺序:
1.在子类中使用__str__,先找子类的__str__,没有的话要向上找,只要父类不是object,就执行父类的__str__
2.但是如果除了object之外的父类都没有__str__方法,就执行子类的__repr__方法
3.如果子类没有__repr__方法,还要向上找父类中的__repr__方法,一直找不到再执行object类中的__str__方法
class Foo1(): # 运用在print打印时触发 def __str__(self): return "自定义打印信息" # 运用在解释器时触发 def __repr__(self): return "自定义打印信息2" obj = Foo1() print(obj) # 自定义打印信息 # __str__和 __repr__共存,print先找str没有再找repr print(obj) # 自定义打印信息
class B: def __str__(self): return 'str : class B' def __repr__(self): return 'repr : class B' b=B() print('%s' % b) print('%r' % b)
8.格式化字符串方法 __format__
# __format__ 格式化方法示例 format_dic = { "ymd": "{0.year}{0.mon}{0.day}", "m-d-y": "{0.mon}-{0.day}-{0.year}", } class Date: def __init__(self, year, mon, day): self.year = year self.mon = mon self.day = day def __format__(self, format_spec): print("-->", format_spec) if not format_spec or format_spec not in format_dic: format_spec = "ymd" fm = format_dic[format_spec] return fm.format(self) d1 = Date(2019, 4, 13) print("{0.year}{0.mon}{0.day}".format(d1)) # 2019413 print(format(d1, "ymd")) # 2019413 print(format(d1, "m-d-y")) # 4-13-2019
9.描述符方法(__get__, __set__, __delete__)
1.描述符概述(描述符协议):
描述符本质就是一个新式类,在这个新式类中,至少实现了__get__(),__set__(),__delete__()中的一个
__get__(): 调用一个属性时,触发
__set__(): 为一个属性赋值时,触发
__delete__(): 使用del删除属性时,触发
数据描述符: 至少实现了__get__()和__set__()
非数据描述符: 没有实现: __set__()
2.描述符应用示例
class Foo4(): def __get__(self, instance, owner): print("get") def __set__(self, instance, value): print("set") def __delete__(self, instance): print("delete") class Foo5(Foo4): name = Foo4() f5 = Foo5() f5.name # get f5.name = 1 # set del f5.name # delete
class Typed: def __init__(self, key, expected_type): self.key = key self.expected_type = expected_type def __get__(self, instance, owner): print('get方法') # print('instance参数【%s】' %instance) # print('owner参数【%s】' %owner) return instance.__dict__[self.key] def __set__(self, instance, value): print('set方法') # print('instance参数【%s】' % instance) # print('value参数【%s】' % value) # print('====>',self) if not isinstance(value,self.expected_type): # print('你传入的类型不是字符串,错误') # return raise TypeError('%s 传入的类型不是%s' %(self.key, self.expected_type)) instance.__dict__[self.key] = value def __delete__(self, instance): print('delete方法') # print('instance参数【%s】' % instance) instance.__dict__.pop(self.key) class People: name = Typed('name', str) # t1.__set__() self.__set__() age = Typed('age', int) # t1.__set__() self.__set__() def __init__(self, name, age, salary): self.name = name self.age = age self.salary = salary p1 = People("coco", 13, 13.3) print(p1.__dict__) # {'name': 'coco', 'age': 13, 'salary': 13.3}
3.描述符的优先级
类属性 > 数据描述符 > 实例属性 > 非数据描述符 > 找不到的属性触发__getattr__()
1.类属性 > 数据描述符
#描述符Str class Str: def __get__(self, instance, owner): print('Str调用') def __set__(self, instance, value): print('Str设置...') def __delete__(self, instance): print('Str删除...') class People: name = Str() def __init__(self, name, age): # name被Str类代理,age被Int类代理 self.name = name self.age = age # 在一个类中定义描述符它就是一个类属性,存在于类的属性字典中,而不是实例的属性字典 # 描述符被定义成了一个类属性,直接通过类名也可以调用类属性name,本质就是在调用描述符Str,触发了__get__() People.name # Str调用 # 赋值并没有触发__set__() People.name = 'coco' # del也没有触发__delete__() del People.name '''原因: 描述符在使用时被定义成另外一个类的类属性,因而类属性比二次加工的描述符伪装而来的类属性有更高的优先级 People.name # 调用类属性name,找不到就去找描述符伪装的类属性name,触发了__get__() People.name = 'coco' # 直接赋值了一个类属性,它拥有更高的优先级,相当于覆盖了描述符不会触发描述符的__set__() del People.name # 直接删除一个类属性,它拥有更高的优先级,相当于覆盖了描述符不会触发描述符的__delete__() '''
2.数据描述符 > 实例属性
#描述符Str class Str: def __get__(self, instance, owner): print('Str调用') def __set__(self, instance, value): print('Str设置...') def __delete__(self, instance): print('Str删除...') class People: name=Str() def __init__(self, name, age): # name被Str类代理,age被Int类代理 self.name = name self.age = age p1 = People('angels', 18) # Str设置... # 如果描述符是一个数据描述符(即有__get__又有__set__) # 那么p1.name的调用与赋值都是触发描述符的操作,与p1本身无关了相当于覆盖了实例的属性 p1.name ='coco' # # Str设置... p1.name # Str调用 # 实例的属性字典中没有name,因为name是一个数据描述符,优先级高于实例属性 # 查看/赋值/删除都是跟描述符有关,与实例无关 print(p1.__dict__) # {'age': 18} del p1.name # Str删除...
3.实例属性 > 非数据描述符
class Foo: def __set__(self, instance, value): print('set') def __get__(self, instance, owner): print('get') class Room: name = Foo() def __init__(self, name, width, length): self.name = name self.width = width self.length = length # name是一个数据描述符,因为name = Foo()而Foo实现了get和set方法,因而比实例属性有更高的优先级 # 对实例的属性操作,触发的都是描述符的方法 r1 = Room('厕所', 1, 1) # set r1.name # get r1.name = '厨房' # set class Foo: def __get__(self, instance, owner): print('get') class Room: name = Foo() def __init__(self, name, width, length): self.name = name self.width = width self.length = length # name是一个非数据描述符,因为name=Foo()而Foo没有实现set方法,因而比实例属性有更低的优先级 # 对实例的属性操作,触发的都是实例自己的方法 r1 = Room('厕所', 1, 1) r1.name r1.name = '厨房'
4.非数据描述符 > 找不到
class Foo: def func(self): print('func被调用') def __getattr__(self, item): print('找不到就调用getattr', item) f1=Foo() f1.xxx # 找不到就调用getattr xxx
4.用描述符定制 静态属性@property, 类方法@classmethod, 静态方法@staticmethod
1.定制静态属性@property
class Lazyproperty: def __init__(self, func): print('==========>', func) self.func = func def __get__(self, instance, owner): print('get') # print(instance) # print(owner) if instance is None: return self res = self.func(instance) setattr(instance, self.func.__name__, res) return res class Room: def __init__(self, name, width, length): self.name = name self.width = width self.length = length # @property #area = property(area) @Lazyproperty #area = Lazypropery(area) def area(self): return self.width * self.length @property #test = property(test) def area1(self): return self.width * self.length r1 = Room("coco", 10, 10) # ======> <function Room.area at 0x0000000006AC4C80> print(r1.area1) # 100 # 先从自己的属性字典找,没有再去类的中找,然后触发了area的__get__方法 print(r1.area) # 先打印get, 再打印100 # 先从自己的属性字典找,找到了,是上次计算的结果,这样就不用每执行一次都去计算 print(r1.area) # 先打印100, 再打印get print(Room.area) # <__main__.Lazyproperty object at 0x0000000005AE52B0> print(Room.area1) # <property object at 0x00000000052F1458>
2.定制类方法@classmethod
class ClassMethod: def __init__(self, func): self.func = func # 类来调用,instance为None,owner为类本身,实例来调用,instance为实例,owner为类本身 def __get__(self, instance, owner): def feedback(): print('在这里可以加功能啊...') return self.func(owner) return feedback class People: name = 'coco' @ClassMethod # say_hi = ClassMethod(say_hi) def say_hi(cls): print('你好啊,world %s' % cls.name) People.say_hi() p1 = People() p1.say_hi() class ClassMethod: def __init__(self, func): self.func = func # 类来调用,instance为None,owner为类本身,实例来调用,instance为实例,owner为类本身 def __get__(self, instance, owner): def feedback(*args, **kwargs): print('在这里可以加功能啊...') return self.func(owner, *args, **kwargs) return feedback class People: name = 'linhaifeng' @ClassMethod # say_hi = ClassMethod(say_hi) def say_hi(cls, msg): print('你好啊,world %s %s' %(cls.name, msg)) People.say_hi('每天都是充满希望的一天') p1 = People() p1.say_hi('每天都是充满希望的一天')
3.定制静态方法@staticmethod
class StaticMethod: def __init__(self, func): self.func = func # 类来调用,instance为None,owner为类本身,实例来调用,instance为实例,owner为类本身 def __get__(self, instance, owner): def feedback(*args, **kwargs): print('在这里可以加功能啊...') return self.func(*args, **kwargs) return feedback class People: @StaticMethod # say_hi = StaticMethod(say_hi) def say_hi(x, y, z): print('------>', x, y, z) People.say_hi(1, 2, 3) p1 = People() p1.say_hi(4, 5, 6)
10.上下文管理器方法(__enter__, __exit__)
上下文管理器内部必须实现 __enter__ __exit__ 方法
class Open(): def __init__(self, name): self.name = name def __enter__(self): print("执行了enter") return self def __exit__(self, exc_type, exc_val, exc_tb): print("执行了exit") print(exc_type) # 异常类型 print(exc_val) # 异常值 print(exc_tb) # 异常追踪信息 with Open("a.txt") as f: print(f) # <__main__.Foo6 object at 0x000000000358E0F0> print(f.name) # a.txt print("结束") # 结束
class A: def __enter__(self): print('before') def __exit__(self, exc_type, exc_val, exc_tb): print('after') with A() as a: print('123')
class A: def __init__(self): print('init') def __enter__(self): print('before') def __exit__(self, exc_type, exc_val, exc_tb): print('after') with A() as a: print('123')
class Myfile: def __init__(self, path, mode='r', encoding='utf-8'): self.path = path self.mode = mode self.encoding = encoding def __enter__(self): self.f = open(self.path, mode=self.mode, encoding=self.encoding) return self.f def __exit__(self, exc_type, exc_val, exc_tb): self.f.close() with Myfile('file',mode='w') as f: f.write('wahaha')
import pickle class MyPickledump: def __init__(self, path): self.path = path def __enter__(self): self.f = open(self.path, mode='ab') return self def dump(self, content): pickle.dump(content, self.f) def __exit__(self, exc_type, exc_val, exc_tb): self.f.close() class Mypickleload: def __init__(self, path): self.path = path def __enter__(self): self.f = open(self.path, mode='rb') return self def __exit__(self, exc_type, exc_val, exc_tb): self.f.close() def load(self): return pickle.load(self.f) def loaditer(self): while True: try: yield self.load() except EOFError: break # with MyPickledump('file') as f: # f.dump({1, 2, 3, 4}) with Mypickleload('file') as f: for item in f.loaditer(): print(item)
import pickle class MyPickledump: def __init__(self, path): self.path = path def __enter__(self): self.f = open(self.path, mode='ab') return self def dump(self, content): pickle.dump(content, self.f) def __exit__(self, exc_type, exc_val, exc_tb): self.f.close() class Mypickleload: def __init__(self, path): self.path = path def __enter__(self): self.f = open(self.path, mode='rb') return self def __exit__(self, exc_type, exc_val, exc_tb): self.f.close() def __iter__(self): while True: try: yield pickle.load(self.f) except EOFError: break # with MyPickledump('file') as f: # f.dump({1, 2, 3, 4}) with Mypickleload('file') as f: for item in f: print(item)
11.哈希方法 __hash__
哈希方法概述:
1.Python底层数据结构是基于hash值寻址的优化操作
2.hash是一个算法能够把某一个要存在内存里的值通过一系列计算,保证不同值的hash结果是不一样的
class A: def __init__(self): self.a = 1 self.b = 2 def __hash__(self): return hash(str(self.a) + str(self.b)) a = A() print(hash(a)) # -7282248652925619321 hash("12") # -7282248652925619321
12.__len__ 方法
__len__ 方法用于获取一个对象的元素个数
class B: def __len__(self): print("len函数触发__len__方法") return 0 b = B() # len一个对象就会触发 __len__方法 len(b) """len(b)执行结果 len函数触发__len__方法 0 """
class A: def __init__(self): self.a = 1 self.b = 2 def __len__(self): print(self.__dict__) # {'a': 1, 'b': 2} return len(self.__dict__) # 2 a = A() print(len(a)) # 2
13.实现迭代器协议的 __next__ 和 __iter__ 方法
方法概述:
__iter__ 方法用于迭代器,列表字典元组可以进行for循环,也是因为类型内部定义了__iter__ 方法
__next__ 方法访问可迭代对象的数据,如果没有数据则触发 StopIteration 异常来通知调用者停止迭代即迭代器协议
class Foo(object): def __init__(self, sq): self.sq = sq def __iter__(self): return iter(self.sq) obj = Foo([11, 22, 33, 44]) for i in obj: print(i, end=" ") # 11 22 33 44
# 模拟range函数加上步长 class Range: def __init__(self, start, stop, step): self.start = start self.stop = stop self.step = step def __next__(self): if self.start >= self.stop: raise StopIteration x = self.start self.start += self.step return x def __iter__(self): return self for i in Range(1, 7, 3): print(i)
# 斐波拉契数列 class Fib: def __init__(self): self._a = 0 self._b = 1 def __iter__(self): return self def __next__(self): self._a, self._b = self._b, self._a + self._b return self._a f1=Fib() print(f1.__next__()) print(next(f1)) print(next(f1)) for i in f1: if i > 100: break print('%s ' % i, end=' ')
14.__getattribute__ 方法
方法概述:
1.当 __getattribute__ 与 __getattr__ 同时存在,只会执行 __getattribute__
2.当 __getattribute__ 在执行过程中抛出异常 AttributeError 时才会同时执行 __getattr__
class Foo: def __init__(self, name): self.name = name def __getattr__(self, item): print('__getattr__执行') # return self.__dict__[item] def __getattribute__(self, item): print('无论如何都会执行') raise AttributeError('发生了异常') f1 = Foo("Coco") f1.x f1.xx
14.单例的实现
单例概述: 单例类从头到尾只能有一个实例且从头到尾之开辟了一块儿属于对象的空间
class Single: __ISINCTANCE = None def __new__(cls, *args, **kwargs): if not cls.__ISINCTANCE: cls.__ISINCTANCE = object.__new__(cls) return cls.__ISINCTANCE def __init__(self, name, age): # self.name = name # self.age = age print(self) s1 = Single('coco', 23) # <__main__.Single object at 0x10f67fad0> s2 = Single('gogo', 40) # <__main__.Single object at 0x10f67fad0>
class MusicPlayer(object): # 记录第一个被创建对象的引用 instance = None # 记录是否执行过初始化动作 init_flag = False def __new__(cls, *args, **kwargs): # 1.判断类属性是否是空对象 if cls.instance is None: # 2.调用父类方法,为第一个对象分配空间 cls.instance = super().__new__(cls) # 3.返回类属性保存的对象引用 return cls.instance def __init__(self): # 1.判断是否执行过初始化动作 if MusicPlayer.init_flag: return # 2.没有执行过,再执行初始化动作 print("初始化播放器") # 3.修改类属性标记 MusicPlayer.init_flag = True player1 = MusicPlayer() # 初始化播放器 print(player1) # <__main__.MusicPlayer object at 0x0000000006AA28D0> player2 = MusicPlayer() print(player2) # <__main__.MusicPlayer object at 0x0000000006AA28D0>
15.函数模拟面向对象设计与类的面向对象设计对比
1.函数模拟面向对象设计
def dogs(name, gender, type): # 狗的属性 def init(name, gender, type): dog = { "name": name, "gender": gender, "type": type, "run": run } return dog # 狗的方法 def run(dog): print("一条狗%s正在跑" % dog["name"]) return init(name, gender, type) d1 = dogs("wangcai", "公", "中华田园犬") d2 = dogs("xiaoqiang", "公", "哈士奇") print("面向对象设计", d1) d1["run"](d1) print(d2["name"])
2.类实现面向对象设计
# 面向对象编程-->用面向对象独有的class语法实现面向对象设计 class Dog: """定义一个狗类""" def __init__(self,name, gender, type): self.name = name self.gender = gender self.type = type def run(self): print("一条狗%s正在跑" % self.name) dog1 = Dog("wangcai", "公", "中华田园犬") dog2 = Dog("xiaoqiang", "公", "哈士奇") print("面向对象编程", dog1.__dict__) # __dict__ 查看属性字典 print(dog1.__dict__["type"]) # 中华田园犬 # 创建出的实例只有数据属性,函数属性实际是调的类的函数属性 dog1.run() print(dog2.name) # 查看类的名字 print(Dog.__name__) # Dog # 查看文档说明 print(Dog.__doc__) # 定义一个狗类 # 查看第一个父类 print(Dog.__base__) # <class 'object'> # 查看所有父类,构成元组 print(Dog.__bases__) # (<class 'object'>,) # 查看类的属性,返回一个字典 print(Dog.__dict__) # 查看类定义所在的模块 print(Dog.__module__) # __main__ # 查看实例对应的类 print(Dog.__class__) # <class 'type'>
16.组合
组合概述: 给一个类的对象封装一个属性,这个属性是另一个类的对象
class GameRole: """英雄人物类""" def __init__(self, name, ad, hp): self.name = name self.ad = ad self.hp = hp def attack(self,p): p.hp = p.hp - self.ad print('%s 攻击 %s,%s 掉了%s血,还剩%s血' %(self.name, p.name, p.name, self.ad, p.hp)) def armament_weapon(self, wea): self.wea = wea class Weapon: """武器属性类""" def __init__(self, name, ad): self.name = name self.ad = ad def fight(self, p1, p2): p2.hp = p2.hp - self.ad print('%s 用%s打了%s\t%s 掉了%s血,还剩%s血'\ % (p1.name, self.name, p2.name, p2.name, self.ad, p2.hp)) p1 = GameRole('coco', 20, 500) p2 = GameRole('cat', 50, 200) axe = Weapon('三板斧', 60) broadsword = Weapon('屠龙宝刀', 100) p1.armament_weapon(axe) # 给coco装备了三板斧这个对象. p1.wea.fight(p1, p2) # coco 用三板斧打了cat cat 掉了60血,还剩140血
# 组合实现与类的关联 class School: def __init__(self, name, addr): self.name = name self.addr = addr class Course: def __init__(self, name, price, school): self.name = name self.price = price self.school = school s1 = School("coco", "成都") s2 = School("coco", "广州") msg = """ 1 coco 成都 2 coco 广州 """ while True: print(msg) menu = { "1": s1, "2": s2 } choice = input("请选择学校>>>:") school_obj = menu[choice] name = input("课程名: ") price = input("课程费用: ") new_course = Course(name, price, school_obj) print("课程 %s 属于 %s 学校" % (new_course.name, new_course.school.name))
17.封装 enclosure
封装概述:
1.封装是指隐藏类的实现细节让使用者不关心这些细节,目的是让使用者通过尽可能少的使用实例属性操作对象
2.私有成员有两种: 私有属性和私有方法,python类中以双下划线('__')开头且不以双下划线结尾的标识符为私有成员
3.私有成员只能被方法调用,不能在子类或其它地方使用
4.广义的封装: 把属性和方法封装在类中,定义一个规范来描述一类事物
5.狭义的封装: 私有化,只能在类的内部访问
class A: def __init__(self): self.__p1 = 100 # 创建私有属性 def __m1(self): print("A类的私有方法被调用!") def test(self): print(self.__p1) # 可以访问 self.__m1() # 可以访问 a = A() # print(a.__p1) # 出错,不可以访问 # a.__m1() # 出错, 在类外部不能调用类的私有方法 a.test() # 用方法来操作私有属性和私有方法 a.__dict__ # {'_A__p1': 100} a._A__p1 # 100 a._A__m1() # A类的私有方法被调用!
class Parent: def __func(self): print('in Parent func') def __init__(self): self.__func() class Son(Parent): def __func(self): print('in Son func') son1 = Son() # in Parent func
18.继承 inheritance 和派生 derived
1.继承和派生概述:
1.继承是从已有类中派生出新类,新类具有原类的数据属性和行为,并能扩展新的能力
2.派生就是从一个已有的类衍生出新类,在新的类上添加新的属性和行为
3.任何类都直接或间接的继承自object类,object类是一切类的超类
4.用继承派生机制,可以将一些共有功能加在基类中实现代码的共享,在不改变超类的代码的基础上改变原有的功能
5.继承和派生流程图: https://www.processon.com/view/link/5ee0d5cc07912929cb38f831
6.术语名词: 基类(base class) 超类(super class) 父类(father class) 派生类(derived class) 子类(child class)
2.单继承:
语法:
class 类名(超类名):
语句块
class Human: # 人 def say(self, what): # 说话的行为 print("说: ", what) def walk(self, distance): # 走路的行为 print("走了", distance, "公里") h1 = Human() h1.say("今天天气不错!") h1.walk(5) class Student(Human): # def say(self, what): # 说话的行为 # print("说: ", what) # def walk(self, distance): # 走路的行为 # print("走了", distance, "公里") def study(self, subject): print("正在学习", subject) s1 = Student() s1.say("今天晚饭吃什么?") s1.walk(3) s1.study("python")
3.多继承 multiple inheritance
多继承概述:
1.多继承是指一个子类继承自两个或两个以上的基类
2.多继承的问题(缺陷): 标识符(名字空间)冲突的问题,要谨慎使用多继承
语法:
class 类名(超类名1, 超类名2, ...):
pass
class Car: def run(self, speed): print("汽车以", speed, "km/h的速度行驶") class Plane: def fly(self, height): print("飞机以海拔", height, "米的高度飞行") class PlaneCar(Car, Plane): """飞行汽车类, 同时继承 自Car和 Plane""" pass p1 = PlaneCar() p1.fly(10000) # 飞机以海拔 10000 米的高度飞行 p1.run(299) # 汽车以 299 km/h的速度行驶
# 多继承缺陷示例 # 小张写了一个类A class A: def m(self): print("A.m()被调用") # 小李写了一个类B: class B: def m(self): print("B.m() 被调用") # 小王感觉小张和小李写的两个类自己可以用 class AB(A, B): pass ab = AB() ab.m() # 调用A类中的m方法
4.多继承的 MRO (Method Resolution Order)问题
MRO 方法搜索顺序问题
子类继承父类时MRO查找顺序为C3算法
MRO顺序-广度优先和深度优先流程图: https://www.processon.com/view/link/5ee103c907912929cb39509a
python 3 广度优先: 一条路走到倒数第二级,判断如果其他路能走到终点,则返回走另一条路,如果不能则走到终点
python 2 深度优先: 一条路走到底
class E: pass class F: pass class A(E): pass class B(E): pass class C(F): pass class D(F): pass class AB(A, B): pass class CD(C, D): pass class ABCD(AB, CD): pass print(ABCD.__mro__) """Python3运行结果 (__main__.ABCD, __main__.AB, __main__.A, __main__.B, __main__.E, __main__.CD, __main__.C, __main__.D, __main__.F, object) """
5.子类调用父类方法
print("******多继承使用类名.__init__ 发生的状态******") class Parent(object): def __init__(self, name): print('parent的init开始被调用') self.name = name print('parent的init结束被调用') class Son1(Parent): def __init__(self, name, age): print('Son1的init开始被调用') self.age = age Parent.__init__(self, name) print('Son1的init结束被调用') class Son2(Parent): def __init__(self, name, gender): print('Son2的init开始被调用') self.gender = gender Parent.__init__(self, name) print('Son2的init结束被调用') class Grandson(Son1, Son2): def __init__(self, name, age, gender): print('Grandson的init开始被调用') Son1.__init__(self, name, age) # 单独调用父类的初始化方法 Son2.__init__(self, name, gender) print('Grandson的init结束被调用') gs = Grandson('grandson', 12, '男') print('姓名: ', gs.name) print('年龄: ', gs.age) print('性别: ', gs.gender) print("******多继承使用类名.__init__ 发生的状态******\n\n")
print("******单继承使用super().__init__ 发生的状态******") class Parent(object): def __init__(self, name): print('parent的init开始被调用') self.name = name print('parent的init结束被调用') class Son1(Parent): def __init__(self, name, age): print('Son1的init开始被调用') self.age = age super().__init__(name) # 单继承不能提供全部参数 print('Son1的init结束被调用') class Grandson(Son1): def __init__(self, name, age, gender): print('Grandson的init开始被调用') super().__init__(name, age) # 单继承不能提供全部参数 print('Grandson的init结束被调用') gs = Grandson('grandson', 12, '男') print('姓名: ', gs.name) print('年龄: ', gs.age) #print('性别: ', gs.gender) print("******单继承使用super().__init__ 发生的状态******\n\n")
print("******多继承使用super().__init__ 发生的状态******") class Parent(object): def __init__(self, name, *args, **kwargs): # 为避免多继承报错,使用不定长参数,接受参数 print('parent的init开始被调用') self.name = name print('parent的init结束被调用') class Son1(Parent): # 为避免多继承报错,使用不定长参数,接受参数 def __init__(self, name, age, *args, **kwargs): print('Son1的init开始被调用') self.age = age super().__init__(name, *args, **kwargs) # 为避免多继承报错,使用不定长参数,接受参数 print('Son1的init结束被调用') class Son2(Parent): # 为避免多继承报错,使用不定长参数,接受参数 def __init__(self, name, gender, *args, **kwargs): print('Son2的init开始被调用') self.gender = gender super().__init__(name, *args, **kwargs) # 为避免多继承报错,使用不定长参数,接受参数 print('Son2的init结束被调用') class Grandson(Son1, Son2): def __init__(self, name, age, gender): print('Grandson的init开始被调用') # 多继承时,相对于使用类名.__init__方法,要把每个父类全部写一遍 # 而super只用一句话,执行了全部父类的方法,这也是为何多继承需要全部传参的一个原因 # super(Grandson, self).__init__(name, age, gender) super().__init__(name, age, gender) print('Grandson的init结束被调用') print(Grandson.__mro__) gs = Grandson('grandson', 12, '男') print('姓名: ', gs.name) print('年龄: ', gs.age) print('性别: ', gs.gender) print("******多继承使用super().__init__ 发生的状态******\n\n")
19.多态 polymorphic
多态概述:
1.多态是指在有继承/派生关系的类中,调用基类对象的方法,实际能调用子类的覆盖方法的现象叫多态
2.多态是不同的子类对象调用相同的父类方法产生不同的执行结果,即多态要以继承和重写父类方法为前提
3.多态是一种类型的多种形态,多个子类去继承父类,那么每一个子类都是父类的一种形态
4.Python的全部对象只有"运行时状态(动态)"没有"C++/Java"里的"编译时状态(静态)"
5.Python中没有多态但处处是多态,因为Python中所有的类都继承自object基类
6.Python要实现静态就只能在防范命名时不要覆盖基类的方法
class Shape: def draw(self): pass class Point(Shape): def draw(self): print("正在画一个点") class Circle(Point): def draw(self): print("正在画一个圆") def my_draw(s): s.draw() # 根据实例的类型在运行时动态决定调用的方法,这样的行为为动态 s1 = Circle() s2 = Point() my_draw(s1) # 正在画一个圆 my_draw(s2) # 正在画一个点
class MiniOS(object): """MiniOS 操作系统类 """ def __init__(self, name): self.name = name self.apps = [] # 安装的应用程序名称列表 def __str__(self): return "%s 安装的软件列表为 %s" % (self.name, str(self.apps)) def install_app(self, app): # 判断是否已经安装了软件 if app.name in self.apps: print("已经安装了 %s,无需再次安装" % app.name) else: app.install() self.apps.append(app.name) class App(object): def __init__(self, name, version, desc): self.name = name self.version = version self.desc = desc def __str__(self): return "%s 的当前版本是 %s - %s" % (self.name, self.version, self.desc) def install(self): print("将 %s [%s] 的执行程序复制到程序目录..." % (self.name, self.version)) class PyCharm(App): pass class Chrome(App): def install(self): print("正在解压缩安装程序...") super().install() linux = MiniOS("Linux") print(linux) pycharm = PyCharm("PyCharm", "1.0", "python 开发的 IDE 环境") chrome = Chrome("Chrome", "2.0", "谷歌浏览器") linux.install_app(pycharm) linux.install_app(chrome) linux.install_app(chrome) print(linux) """执行结果 Linux 安装的软件列表为 [] 将 PyCharm [1.0] 的执行程序复制到程序目录... 正在解压缩安装程序... 将 Chrome [2.0] 的执行程序复制到程序目录... 已经安装了 Chrome,无需再次安装 Linux 安装的软件列表为 ['PyCharm', 'Chrome'] """
20.鸭子类型 Duck typing
鸭子类型概述:
1.一个对象有效的语义,不是由继承自特定的类或实现特定的接口,而是由"当前方法和属性的集合"决定
2.鸭子类型是动态类型的一种风格,看着像鸭子,它就是鸭子
3.鸭子类型源自鸭子测试: 当看到一只鸟走起来像鸭子,游泳起来像鸭子,叫起来也像鸭子,那么这只鸟就可以被称为鸭子
4.鸭子类型中的规范全凭自觉,没有强制规定,我们不需要关心对象是什么类型到底是不是鸭子,只需要关心对象的行为
# Srt,List,Tuple这些类都有相同的方法,那么这些类互称鸭子 class Str: def index(self): pass class List: def index(self): pass class Tuple: def index(self): pass s = Str() s.index() l = List() l.index() t = Tuple() t.index() # Str类,List类和Tuple类完全没有耦合性,但是在某种意义上他们却统一了一个标准 # 对相同的功能设定了相同的名字,这样方便开发,这个方法就可以互成为鸭子类型
# 用鸭子类型实现多态 class Duck: def quack(self): print "Duck imitate duck" class Bird: def quack(self): print "bird imitate duck" class Chicken: def quack(self): print "chicken imitate duck" def in_the_forest(duck): duck.quack() duck = Duck() bird = Bird() chicken = Chicken() for x in [duck, bird, chicken]: in_the_forest(x)
21.包装(二次加工标准类型)
包装概述: 基于标准数据类型来定制我们自己的数据类型,新增/改写方法
# 包装 = 继承 + 派生 class List(list): # 定义一个方法取列表中间值 def show_midlle(self): min_index = int(len(self) / 2) return self[min_index] # 重写列表的append方法,限制只允许添加字符串 def append(self, p_object): if type(p_object) is str: super().append(p_object) else: print("类型错误,只允许添加字符串") l1 = List("helloworld") print(l1, type(l1)) # ['h', 'e', 'l', 'l', 'o', 'w', 'o', 'r', 'l', 'd'] <class '__main__.List'> print("列表%s的中间值是%s" %(l1, l1.show_midlle())) # 列表['h', 'e', 'l', 'l', 'o', 'w', 'o', 'r', 'l', 'd']的中间值是w l1.append("coco") l1.append(123) # 类型错误,只允许添加字符串 print(l1) # ['h', 'e', 'l', 'l', 'o', 'w', 'o', 'r', 'l', 'd', 'coco']
# 给clear加权限限制 class List(list): def __init__(self, item, tag=False): super().__init__(item) self.tag = tag def append(self, p_object): if not isinstance(p_object, str): raise TypeError super().append(p_object) def clear(self): if not self.tag: raise PermissionError super().clear() l=List([1, 2, 3], False) print(l) print(l.tag) l.append('saf') print(l) # l.clear() #异常 l.tag = True l.clear()
22.授权
授权概述:
1.授权是包装的一个特性,包装一个类型通常是对已存在的类型的一些定制
2.授权的这种做法可以新建修改或删除原有产品的功能,其它的则保持原样
3.授权的过程,即所有更新的功能都是由新类的某部分来处理,但已存在的功能就授权给对象的默认属性
4.实现授权的关键点就是覆盖__getattr__方法
# 授权-->是组合的运用 import time class Open: def __init__(self, filename, mode="r", encoding="utf-8"): self.filename = filename self.file = open(filename, mode, encoding=encoding) self.mode = mode self.encoding = encoding def write(self, line): t = time.strftime("%Y-%m-%d %X") self.file.write("%s %s" % (t, line)) def __getattr__(self, item): return getattr(self.file, item, "没有找到%s" % item) f1 = Open("a.txt", "r+") print(f1.file) # <_io.TextIOWrapper name='a.txt' mode='r+' encoding='utf-8'> print(f1.reads) # 没有找到reads print(f1.read) # <built-in method read of _io.TextIOWrapper object at 0x00000000056F51F8> f1.write("hello world\n") f1.seek(0) # 移动文件指针到文件开头 print("读文件%s内容是%s" % (f1.filename, f1.read())) # 读文件a.txt内容是2020-07-31 10:54:27 hello world
# 加上b模式支持 import time class FileHandle: def __init__(self, filename, mode='r', encoding='utf-8'): if 'b' in mode: self.file = open(filename, mode) else: self.file = open(filename, mode, encoding=encoding) self.filename = filename self.mode = mode self.encoding = encoding def write(self, line): if 'b' in self.mode: if not isinstance(line, bytes): raise TypeError('must be bytes') self.file.write(line) def __getattr__(self, item): return getattr(self.file, item) def __str__(self): if 'b' in self.mode: res = "<_io.BufferedReader name='%s'>" %self.filename else: res = ("<_io.TextIOWrapper name='%s' mode='%s' encoding='%s'>" % (self.filename, self.mode, self.encoding)) return res f1 = FileHandle('b.txt','wb') # f1.write('你好啊') # 自定制的write,不用在进行encode转成二进制去写了 f1.write('你好啊'.encode('utf-8')) print(f1) # <_io.BufferedReader name='b.txt'> f1.close()
class List: def __init__(self, seq): self.seq = seq def append(self, p_object): '派生自己的append加上类型检查,覆盖原有的append' if not isinstance(p_object, int): raise TypeError('must be int') self.seq.append(p_object) @property def mid(self): '新增自己的方法' index = len(self.seq) // 2 return self.seq[index] def __getattr__(self, item): return getattr(self.seq,item) def __str__(self): return str(self.seq) l = List([1, 2, 3]) print(l) # [1, 2, 3] l.append(4) print(l) # [1, 2, 3, 4] # l.append('33') # 会报错,必须为int类型 print(l.mid) # 3 # 基于授权,获得insert方法 l.insert(0, -123) print(l) # [-123, 1, 2, 3, 4]
class List: def __init__(self, seq, permission=False): self.seq = seq self.permission = permission def clear(self): if not self.permission: raise PermissionError('not allow the operation') self.seq.clear() def __getattr__(self, item): return getattr(self.seq, item) def __str__(self): return str(self.seq) l = List([1, 2, 3]) # l.clear() # 此时没有权限,抛出异常 l.permission = True print(l) # [1, 2, 3] l.clear() # l.clear() print(l) # [] #基于授权,获得insert方法 l.insert(0, -123) print(l) # [-123]
23.覆盖 override
覆盖概述:
1.覆盖是指在有继承派生关系的类中,子类中实现了与基类(超类)同名的方法
2.在子类实例调用方法时,实际调用的是子类中的覆盖版本,这种现象叫做覆盖
class A: def work(self): print("A类的work方法被调用") class B(A): def work(self): print("B类的work方法被调用") b = B() b.work() # 子类已经覆盖了父类的方法
24.函数重写 overwrite
函数重写概述: 在自定义的类中,通过添加特定的方法,让自定义的类实例化的对象能像内建对象一样进行内建函数操作
1.字符串显示函数重写
repr(obj) 返回一个能代表此对象的字符串的表达式(这个字符串是给Python解释器识别用的)
repr(obj) 函数用于远程通信数据校验 例如: eval(repr(obj)) == obj
str(obj) 通过给定的对象返回一个字符串(这个字符串通常是给人阅读的)
repr(obj) 函数的重写方法 def __repr__(self)
str(obj) 函数的重写方法 def __str__(self)
当对象没有 __str__方法时, 则返回__repr__(self)的值
class B: def __str__(self): return 'str : class B' def __repr__(self): return 'repr : class B' b=B() print('%s'%b) # str : class B print('%r'%b) # repr : class B
2.内建函数重写
obj.__abs__() 对应 abs(obj)
obj.__len__() 对应 len(obj)
obj.__reversed__() 对应 reversed(obj)
obj.__round__() 对应 round(obj)
# 此示例示意 abs(obj) 函数的重写方法 obj.__abs__() 方法的使用 class MyInteger: def __init__(self, value): self.data = value def __repr__(self): return 'MyInteger(%d)' % self.data def __abs__(self): if self.data < 0: return MyInteger(-self.data) # 创建一个新的以对象并返回 return MyInteger(self.data) def __len__(self): '''len(x)函数规定只能返回整数值,因此此方法不能返回字符串等其它类型的值''' return 100 I1 = MyInteger(-10) print(I1) # <-- 此处等同于print(str(I1)) I2 = abs(I1) # I2 = MyInteger(10) print(I2) # MyInteger(10) print(len(I1)) # I1.__len__()
3.数值转换函数重写
obj.__complex__() 对应 complex(obj)
obj.__int__() 对应 int(obj)
obj.__float__() 对应 float(obj)
obj.__bool__() 对应 bool(obj)
4.布尔测试函数的重写
布尔测试函数概述:
1.用于bool(obj)函数取值,用于if语句真值表达式中,用于while语句真值表达式中
2.布测试式方法的查找顺序是 __bool__方法,其次是__len__方法,如果都没有则返回True
3.语法:
def __bool__(self):
pass
# 此示例示意 bool 真值测试函数的重写 class MyList: '''定义一个容器类,用于存储任意类型的数据其内部的存储方式用list实现''' def __init__(self, iterable): self.data = [x for x in iterable] def __repr__(self): return 'MyList(%s)' % self.data def __len__(self): print("__len__ 方法被调用!") return len(self.data) # 返回列表的长度 def __bool__(self): print('__bool__方法被调用') '''此方法用于bool(obj) 函数取值,优先取此函数的返回值,此方法用于定义bool(obj)的取值规则''' # 规则,所有元素的和为0,则返回False否则返回True return sum(self.data) != 0 myl = MyList([1, -2, 5, -4]) print(myl) # MyList([1, -2, 5, -4]) print('bool(myl) =', bool(myl)) if myl: print('myl 的布尔值为True') else: print('myl 的布尔值为False')
5.自定制格式化字符串重写 __format__
format_dict = { 'nat':'{obj.name}-{obj.addr}-{obj.type}', # 学校名-学校地址-学校类型 'tna':'{obj.type}:{obj.name}:{obj.addr}', # 学校类型:学校名:学校地址 'tan':'{obj.type}/{obj.addr}/{obj.name}', # 学校类型/学校地址/学校名 } class School: def __init__(self, name, addr, type): self.name = name self.addr = addr self.type = type def __repr__(self): return 'School(%s,%s)' %(self.name, self.addr) def __str__(self): return '(%s,%s)' %(self.name, self.addr) def __format__(self, format_spec): # if format_spec if not format_spec or format_spec not in format_dict: format_spec = 'nat' fmt = format_dict[format_spec] return fmt.format(obj=self) s1 = School('oldboy1', '北京', '私立') print('from repr: ',repr(s1)) print('from str: ',str(s1)) print(s1) ''' str函数或者print函数--->obj.__str__();repr或者交互式解释器--->obj.__repr__() 如果__str__没有被定义,那么就会使用__repr__来代替输出,这俩方法的返回值必须是字符串,否则抛出异常 ''' print(format(s1, 'nat')) print(format(s1, 'tna')) print(format(s1, 'tan')) print(format(s1, 'asfdasdffd'))
6.反射函数重写
class Foo(): x = 1 def __init__(self): self.y = 1 def __getattr__(self, item): print("你要找的%s属性不存在" % item) # 重写内置删除方法,限制删除条件 def __delattr__(self, item): print("执行了删除%s属性的操作" % item) self.__dict__.pop(item) # 重写内置设置方法,限制设置条件 def __setattr__(self, key, value): print("你执行了设置%s属性值为%s的操作" %(key, value)) self.__dict__[key] = value f1 = Foo() f1.x = 10 f1.name = "coco" # 设置属性时会触发__setattr__ print(f1.z) # 属性不存在时,会自动触发__getattr__ del f1.y # 删除属性时,会触发__delattr__ print(f1.__dict__)
25.运算符重载
运算符重载概述:
1.让自定义的类实例化的对像能够使用运算符进行操作,像数学表达式一样进行运算操作让程序简洁易读
2.运算符重载方法的参数已经有固定的含义,不建议改变原有的含义
1.算术运算符
方法名 运算符
__add__ 加法 +
__sub__ 减法 -
__mul__ 乘法 *
__truediv__ 除法 /
__floordiv__ 地板除 //
__mod__ 取模(求余) %
__pow__ 幂 **
二元运算符重载方法格式:
def __xxx__(self, other):
pass
# 此程序示意运算符重载 class MyNumber: def __init__(self, v): self.data = v def __repr__(self): return "MyNumber(%d)" % self.data def __add__(self, other): print("__add__方法被调用") obj = MyNumber(self.data + other.data) return obj def __sub__(self, other): return MyNumber(self.data - other.data) n1 = MyNumber(100) n2 = MyNumber(200) # n3 = n1.__add__(n2) n3 = n1 + n2 # 等同于 n1.__add__(n2) print(n3) n4 = n2 - n1 print('n4 =', n4) # MyNumber(100)
# 自定义对象的加法运算符重载规则 class MyType: """实现对象中*号求和""" def __init__(self, s): self.s = s def __add__(self, other): return self.s.count('*') + other.s.count('*') obj1 = MyType('ccasdd***2343*****') obj2 = MyType('vvsvvndk***3423**6517***') print(obj1 + obj2) print(obj1.__add__(obj2))
2.反向算术运算符
方法名 运算符
__radd__ 加法 +
__rsub__ 减法 -
__rmul__ 乘法 *
__rtruediv__ 除法 /
__rfloordiv__ 地板除 //
__rmod__ 取模(求余) %
__rpow__ 幂 **
# 此示例示意返向算术运算符的重载 class MyList: def __init__(self, iterable): self.data = [x for x in iterable] def __repr__(self): # %r 等同与用repr函数取值--> repr(self.data) return 'MyList(%r)' % self.data def __mul__(self, rhs): return MyList(self.data * rhs) def __rmul__(self, lhs): print("__rmul__被调用, lhs=", lhs) return MyList(self.data * lhs) # lhs (left hand side) L1 = MyList([1, 2, 3]) L2 = MyList(range(4, 7)) L4 = L1 * 2 # 实现乘法运算调用的是自定义的__mul__方法 print('L4 =', L4) # MyList([1,2,3,1,2,3]) L5 = 2 * L1 # 此时调用的是自定义的反向乘法运算符重载__rmul__方法 print(L5)
3.复合赋值运算符重载
方法名 运算符
__iadd__ 加法 +=
__isub__ 减法 -=
__imul__ 乘法 *=
__itruediv__ 除法 /=
__ifloordiv__ 地板除 //=
__imod__ 取模(求余) %=
__ipow__ 幂 **=
示例:
# 此示例示意复合赋值算术运算符的重载 class MyList: def __init__(self, iterable): self.data = [x for x in iterable] def __repr__(self): return 'MyList(%r)' % self.data def __add__(self, rhs): print("__add__方法被调用") return MyList(self.data + rhs.data) def __iadd__(self, rhs): print("__iadd__方法被调用") self.data.extend(rhs.data) return self L1 = MyList([1, 2, 3]) L2 = MyList(range(4, 7)) print("id(L1) =", id(L1)) # +=优先调用__iadd__方法,没有再调用__add__方法 L1 += L2 # 相当于 L1 = L1 + L2 print('L1 =', L1) print("id(L1) =", id(L1))
4.比较的运算符的重载
__lt__ < 小于
__le__ <= 小于等于
__gt__ > 大于
__ge__ >= 大于等于
__eq__ == 等于
__ne__ != 不等于
比较运算符通常返回True或False
5.位运算符重载
__inert__ ~ 取反(一元运算符)
__and__ & 按位与(交集)
__or__ | 按位或(并集)
__xor__ ^ 按位异或(对称补集)
__lshift__ << 左移
__rshift__ >> 右移
6.反向位运算符重载
__rand__ & 按位与(交集)
__ror__ | 按位或(并集)
__rxor__ ^ 按位异或(对称补集)
__rlshift__ << 左移
__rrshift__ >> 右移
7.复合赋值运算符重载
__iand__ &= 按位与(交集)
__ior__ |= 按位或(并集)
__ixor__ ^= 按位异或(对称补集)
__ilshift__ <<= 左移
__irshift__ >>= 右移
8.一元运算符的重载
__neg__ - 负号
__pos__ + 正号
__invert__ ~ 按位取反
格式:
def __xxx__(self):
pass
# 此示例示意一元运算符的重载 class MyList: def __init__(self, iterable): self.data = [x for x in iterable] def __repr__(self): return 'MyList(%r)' % self.data def __neg__(self): print("__neg__方法被调用!") L = (-x for x in self.data) return MyList(L) L1 = MyList([1, -2, 3, -4, 5]) print("L1 =", L1) L2 = -L1 print("L2 =", L2)
9.成员运算符的重载
in
not in 相当于 in 取反,所有只需要重载 in 即可
格式:
def __contains__(self, e): # e代表元素
pass
# 此示例示意 in / not in 运算符的重载 class MyList: def __init__(self, iterable): self.data = [x for x in iterable] def __repr__(self): return 'MyList(%r)' % self.data def __contains__(self, e): # e 代表测试元素 print("__contains__被调用") for x in self.data: if e == x: # 如果相同,则说明e在列表中 return True return False L1 = MyList([1, -2, 3, -4, 5]) if 2 in L1: # 需要重载 __contains__ 方法 print("2在L1中") else: print("2不在L1中")
10.索引和切片运算符的重载
重载方法: 让自定义的类型的对象能够支持索引和切片操作
__getitem__(self, i) 用于索引/切片取值
__setitem__(self, i) 用于索引/切片赋值
__delitem__(self, i) 用于del语句删除索引操作
# 此示例示意索引 index 和切片 slice 运算符的重载 class MyList: def __init__(self, iterable): self.data = [x for x in iterable] def __repr__(self): return "MyList(%r)" % self.data def __getitem__(self, i): print("__getitem__被调用", i) if type(i) is slice: print("正在进行切片操作") elif type(i) is int: print("正在进行索引操作") return self.data[i] def __setitem__(self, i, v): print("__setitem__被调用", i, v) self.data[i] = v def __delitem__(self, i): print("__delitem__被调用", i) del self.data[i] L1 = MyList([1, -2, 3, -4, 5]) # 切片操作调用的是__getitem__方法,L1[0:2]等同于L1[slice(0, 2, None)] print(L1[0:2]) # [1, -2] print(L1[2]) # 3 L1[1] = 2 # MyList([1, 2, 3, -4, 5]) del L1[:2] print(L1) # MyList([3, -4, 5])
26.类装饰器
1.类的装饰器无参数示例
def decorate(cls): print('类的装饰器开始运行啦------>') return cls @decorate # 无参:People=decorate(People) class People: def __init__(self, name, age, salary): self.name = name self.age = age self.salary = salary p1=People('egon', 18, 3.3)
2.类的装饰器有参数示例
def typeassert(**kwargs): def decorate(cls): print('类的装饰器运行:', kwargs) return cls return decorate # 有参:1.运行typeassert(...)返回结果是decorate,此时参数都传给kwargs # 有参:2.People=decorate(People) @typeassert(name=str, age=int, salary=float) class People: def __init__(self, name, age, salary): self.name = name self.age = age self.salary = salary p1=People('egon', 18, 3.3) # 类的装饰器运行:{'name': <class 'str'>, 'age': <class 'int'>, 'salar': <class 'float'>}
class Test(object): def __init__(self, func): print("---初始化---") print("func name is %s" % func.__name__) self.__func = func def __call__(self): print("---装饰器中的功能---") self.__func() """说明: 1.用Test来装作装饰器对test函数进行装饰的时,先创建Test的实例对象,再把test这个函数名当做参数传递到__init__方法中 即在__init__方法中的属性__func指向了test指向的函数,而test则指向了用Test创建出来的实例对象 2.当在使用test()进行调用时,就相当于让这个对象(),因此会调用这个对象的__call__方法 3.为了能够在__call__方法中调用原来test指向的函数体,在__init__方法中需要一个实例属性来保存这个函数体的引用 所以才有了self.__func = func这句代码,从而在调用__call__方法中能够调用到test之前的函数体 """ @Test def test(): print("----test---") test() """执行结果 ---初始化--- func name is test ---装饰器中的功能--- ----test--- """
3.描述符装饰器示例
class Typed: def __init__(self, key, expected_type): self.key = key self.expected_type = expected_type def __get__(self, instance, owner): print('get方法') # print('instance参数【%s】' % instance) # print('owner参数【%s】' % owner) return instance.__dict__[self.key] def __set__(self, instance, value): print('set方法') # print('instance参数【%s】' % instance) # print('value参数【%s】' % value) # print('====>', self) if not isinstance(value, self.expected_type): # print('你传入的类型不是字符串,错误') # return raise TypeError('%s 传入的类型不是%s' % (self.key, self.expected_type)) instance.__dict__[self.key] = value def __delete__(self, instance): print('delete方法') # print('instance参数【%s】' % instance) instance.__dict__.pop(self.key) def deco(**kwargs): def wrapper(obj): for key,val in kwargs.items(): print("-->", key, val) setattr(obj, key, Typed(key, val)) return obj return wrapper @deco(name=str, age=int) class People: def __init__(self, name, age, salary): self.name = name self.age = age self.salary = salary p1 = People('coco', 13, 13.3) print(p1.__dict__) """执行结果 --> name <class 'str'> --> age <class 'int'> set方法 set方法 {'name': 'coco', 'age': 13, 'salary': 13.3} """
4.类装饰函数示例
class Test: def __init__(self, func): # 要使__call__方法中调用原来get_str指向的函数体,需要在__init__方法中就需要一个实例属性来保存这个函数体的引用 self.__func = func def __call__(self): print("Test类装饰的内容") return self.__func() # 用Test来装作装饰器对get_str函数进行装饰的时候,首先会创建Test的实例对象 # 然后把get_str这个函数名当做参数传递到__init__方法中的属性__func指向了get_str指向的函数 @Test # 等同于get_str = Test(get_str) def get_str(): print("this is get_str function") return "ok" # get_str指向了用Test创建出来的实例对象,当在使用get_str()进行调用时 # 就相当于让这个对象(),因此会调用这个对象的__call__方法 ret = get_str() print(ret) """执行结果 Test类装饰的内容 this is get_str function ok """
27.类的约束-抽象类(接口类)
1.抽象类概述:
1.抽象类的主要功能就是定制一个规范,让其子类必须按照此规范实现,抽象类本身不能被实例化
2.制定了一个规范,让继承父类的子类必须实现父类的抽象方法,如果子类没有实现父类的抽象方法则子类实例化对象时报错
3.抽象类和接口类做的事情: 制定一个类的metaclass是ABCMeta那么这个类就变成了一个抽象类
4.Python中有原生实现抽象类的方法,但没有原生实现接口类的方法
5.在Java来区分抽象类和接口类
1.抽象类: 只能被子类单继承,抽象类的方法可以实现
2.接口类: 可以被子类多继承,接口类的所有方法都不应该实现,只定义方法后用pass占位
3.抽象类和接口类只需要知道区分,不用按照Java规定去写Python中的代码
from abc import ABCMeta from abc import abstractmethod class Payment(metaclass=ABCMeta): # 抽象类规范和约束,metaclass指定的是一个元类 @abstractmethod def pay(self): # 抽象方法 pass class Alipay(Payment): def __init__(self,money): self.money = money def pay(self): print('使用支付宝支付了%s' %self.money) class Jdpay(Payment): def __init__(self, money): self.money = money def pay(self): print('使用京东支付了%s' % self.money) class Wechatpay(Payment): def __init__(self,money): self.money = money def pay(self): print('使用微信支付了%s' % self.money) def pay(obj): obj.pay() # 子类中没有实现pay方法时,子类实例化对象时会报错 w1 = Wechatpay(200) a1 = Alipay(200) j1 = Jdpay(100) # 统一化接口设计,也叫归一化设计: 不管是哪一个类的对象,都调用同一个函数去完成相似的功能 pay(a1) # 使用支付宝支付了200 pay(j1) # 使用京东支付了100
# 用元类的思想来实现约束时子类必须实现pay方法 class Payment: """此类什么都不做,就是制定一个标准,谁继承我就必须定义我里面的方法""" def pay(self, money): raise Exception("你没有实现pay方法") class QQpay(Payment): def pay(self, money): print('使用qq支付%s元' % money) class Alipay(Payment): def pay(self, money): print('使用阿里支付%s元' % money) class Wechatpay(Payment): def fuqian(self, money): print('使用微信支付%s元' % money) def pay(obj,money): obj.pay(money) a = Alipay() b = QQpay() c = Wechatpay() pay(a, 100) # 使用阿里支付100元 pay(b, 200) # 使用qq支付200元 pay(c, 300) # Exception: 你没有实现pay方法
28.类的约束-元类
1.元类概述:
1.元类就是用来创建这些类(对象)的,元类就是类的类,type就是Python在背后用来创建所有类的元类
2.自定义元类目的是拦截类的创建, 修改类, 返回修改之后的类
3.类中有一个属性 __metaclass__ 其用来表示该类由谁来实例化创建,可以用该属性设置一个type类的派生类,从而查看类创建的过程
4.type可以动态创建类,type(类名, 由父类名称组成的元组(针对继承的情况,可以为空), 包含属性的字典(名称和值))
5.元类创建类,元类创建元类验证
class A: pass a = A() a.__class__ # __main__.A a.__class__.__class__ # type a.__class__.__class__.__class__ # type
2.type动态创建类
Test = type("Test", (), {"num1": 100, "num2": 200}) Test.__dict__ obj = Test() obj.num1 obj.num2 """执行结果 mappingproxy({'num1': 100, 'num2': 200, '__module__': '__main__', '__dict__': <attribute '__dict__' of 'Test' objects>, '__weakref__': <attribute '__weakref__' of 'Test' objects>, '__doc__': None}) 100 200 """
3.较为完整的使用type创建类的方式
class A(object): num = 100 def print_b(self): print("实例方法调用", self.num) @staticmethod def print_static(): print("----静态方法调用-----") @classmethod def print_class(cls): print("类方法调用", cls.num) B = type("B", (A,), {"print_b": print_b, "print_static": print_static, "print_class": print_class}) b = B() b.print_b() # 实例方法调用 100 b.print_static() # ----静态方法调用---- b.print_class() # 类方法调用 100
4.自定义元类的 __init__ 和 __call__ 方法
class MyType(type): def __init__(self, a, b, c): print("a:", a) # a: Foo print("b:", b) # b: () print("c:", c) # c: {'__module__': '__main__', '__qualname__': 'Foo', '__init__': <function Foo.__init__ at 0x102ead560>} print("元类初始化执行完成") # 元类初始化执行完成 def __call__(self, *args, **kwargs): print(args, kwargs) # ('coco', 18) {} obj = object.__new__(self) # object.__new__(Foo)-->f1 self.__init__(obj, *args, **kwargs) # Foo.__init__(f1, *args, **kwargs) return obj class Foo(object, metaclass=MyType): # Foo=MyType(Foo, "Foo", (), {})-->__init__ def __init__(self, name, age): self.name = name self.age = age f1 = Foo("coco", 18) print(f1) # <__main__.Foo object at 0x00000000055BF6A0> print(f1.__dict__) # {'name': 'coco', 'age': 18}
5.自定义元类的 __new__ 方法
class UpperAttrMetaClass(type): # __new__ 是在__init__之前被调用的特殊方法,很少用到__new__,除非希望能够控制对象的创建 # __new__是用来创建对象并返回的方法,而__init__只是用来将传入的参数初始化给对象 def __new__(cls, class_name, class_parents, class_attr): # 遍历属性字典,把不是__开头的属性名字变为大写 new_attr = {} for name, value in class_attr.items(): if not name.startswith("__"): new_attr[name.upper()] = value # 方法1: 通过'type'来做类对象的创建 return type(class_name, class_parents, new_attr) # 方法2: 复用type.__new__方法,这就是基本的OOP编程,没什么魔法 # return type.__new__(cls, class_name, class_parents, new_attr) # python3的用法 class Foo(object, metaclass=UpperAttrMetaClass): bar = 'bip' # python2的用法 # class Foo(object): # __metaclass__ = UpperAttrMetaClass # bar = 'bip' print(hasattr(Foo, 'bar')) # False print(hasattr(Foo, 'BAR')) # True f = Foo() print(f.BAR) # bip
29.元类的应用-ORM
1.ORM概述:
1.ORM是python编程语言后端web框架Django的核心思想"Object Relational Mapping",即对象-关系映射,简称ORM
2.创建一个实例对象,用创建它的类名当做数据表名,用创建它的类属性对应数据表的字段,当对这个实例对象操作时,能够对应MySQL语句
3.ORM就是让开发者在操作数据库的时候,能够像操作对象时通过xxxx.属性=yyyy一样简单
2.通过元类简单实现ORM中的insert功能
class ModelMetaclass(type): def __new__(cls, name, bases, attrs): mappings = dict() # 判断是否需要保存 for k, v in attrs.items(): # 判断是否是指定的StringField或者IntegerField的实例对象 if isinstance(v, tuple): print('Found mapping: %s ==> %s' % (k, v)) mappings[k] = v # 删除这些已经在字典中存储的属性 for k in mappings.keys(): attrs.pop(k) # 将之前的uid/name/email/password以及对应的对象引用,类名字 attrs['__mappings__'] = mappings # 保存属性和列的映射关系 attrs['__table__'] = name # 假设表名和类名一致 return type.__new__(cls, name, bases, attrs) class Model(object, metaclass=ModelMetaclass): def __init__(self, **kwargs): for name, value in kwargs.items(): setattr(self, name, value) def save(self): fields = [] args = [] for k, v in self.__mappings__.items(): fields.append(v[0]) args.append(getattr(self, k, None)) args_temp = list() for temp in args: # 判断入如果是数字类型 if isinstance(temp, int): args_temp.append(str(temp)) elif isinstance(temp, str): args_temp.append("""'%s'""" % temp) sql = ('insert into %s (%s) values (%s)' % (self.__table__, ','.join(fields), ','.join(args_temp))) print('SQL: %s' % sql) class User(Model): uid = ('uid', "int unsigned") name = ('username', "varchar(30)") email = ('email', "varchar(30)") password = ('password', "varchar(30)") # 实例化对象首先会调用元类ModelMetaclass的__new__构造方法,再调用父类Model的__init__初始化方法 u = User(uid=12345, name='Michael', email='test@orm.org', password='my-pwd') # print(u.__dict__) u.save()
30.即时通讯和微信面向对象设计示例
1.腾讯即时通信模块,初级封装
#! /usr/bin/env python # coding: utf-8 import random import time class Message(object): def __init__(self, msgarr=[], toacc=''): self.msgbody = msgarr # 此处为MsgDict对象实例的列表或者空列表 self.toacc = toacc # toacc为字符串(单发)或者列表(批量发) self.msgrandom = random.randint(1, 1000000000) self.msgrequest = { 'To_Account': toacc, # 消息接收方账号 'MsgRandom': self.msgrandom, # 消息随机数,由随机函数产生 'MsgBody': [t.msg for t in msgarr] } def del_option(self, option): if option in (set(self.msgrequest) - set(['To_Account', 'MsgRandom', 'MsgBody'])): self.__dict__.pop(option) self.msgrequest.pop(option) def append_msg(self, msg): self.msgbody.append(msg) self.msgrequest['MsgBody'].append(msg.msg) def insert_msg(self, index, msg): self.msgbody.insert(index, msg) self.msgrequest['MsgBody'].insert(msg.msg) def del_msg(self, index): if index in range(len(self.msgbody)): del self.msgbody[index] del sel.msgrequest['MsgBody'][index] def set_from(self, fromacc): # 指定消息的发送方,默认为服务器发送 self.fromacc = fromacc self.msgrequest['From_Account'] = fromacc def set_to(self, toacc): # 指定消息的接收方,可以为String(单发),可以为List(批量发送) self.toacc = toacc self.msgrequest['To_Account'] = toacc def refresh_random(self): self.msgrandom = random.randint(1, 1000000000) self.msgrequest['MsgRandom'] = self.msgrandom, # 消息随机数,由随机函数产生 def set_sync(self, sync): # 同步选项: 1, 把消息同步到From_Account在线终端和漫游上 # 2, 消息不同步至From_Account # 若不填写,默认情况下会将消息同步 # 仅在单发单聊消息中可调用 self.sync = sync self.msgrequest['SyncOtherMachine'] = sync def set_timestamp(self): # 设置消息时间戳,unix时间, 仅在单发单聊消息中可以调用 self.timestamp = int(time.time()) self.msgrequest['MsgTimeStamp'] = self.timestamp def set_offlinepush(self, pushflag=0, desc='', ext='', sound=''): # 仅适用于APNa推送,不适用于安卓厂商推送 self.msgrequest['OfflinePushInfo'] = { 'PushFlag': pushflag, 'Desc': desc, 'Ext': ext, 'Sound': sound } class MsgDict(object): def __init__(self, msgtype='', msgcontent={}): self.msgtype = msgtype self.msgcontent = msgcontent @property def msg(self): return { 'MsgType': self.msgtype, 'MsgContent': self.msgcontent } def set_content(self, content): self.msgcontent = content class TextMsg(MsgDict): def __init__(self, text='', msgtype='TIMTextElem'): self.text = text content = {'Text': text} super(TextMsg, self).__init__(msgtype=msgtype, msgcontent=content) def set_text(self, text): self.text = text self.msgcontent['Text'] = text class LocationMsg(MsgDict): def __init__(self, desc='', latitude=0, longitude=0, msgtype='TIMLocationElem'): self.desc = desc self.latitude = latitude self.longitude = longitude content = { 'Desc': desc, # 地理位置描述信息, String 'Latitude': latitude, # 纬度, Number 'Longitude': longitude # 经度, Number } super(LocationMsg, self).__init__(msgtype=msgtype, msgcontent=content) def set_desc(self, desc): self.desc = desc self.msgcontent['Desc'] = desc def set_location(self, latitude, longitude): self.latitude = latitude self.longitude = longitude self.msgcontent['Latitude'] = latitude self.msgcontent['Longitude'] = longitude def set_latitude(self, latitude): self.latitude = latitude self.msgcontent['Latitude'] = latitude def set_longitude(self, longitude): self.longitude = longitude self.msgcontent['Longitude'] = longitude class FaceMsg(MsgDict): def __init__(self, index=1, data='', msgtype='TIMFaceElem'): self.index = index self.data = data content = { 'Index': index, # 表情索引,用户自定义, Number 'Data': data # 额外数据, String } super(TextMsg, self).__init__(msgtype=msgtype, msgcontent=content) def set_index(self, index): self.index = index self.msgcontent['Index'] = index def set_data(self, data): self.data = data self.msgcontent['Data'] = data class CustomMsg(MsgDict): def __init__(self, data='', desc='', ext='', sound='', msgtype='TIMCustomElem'): self.data = data self.desc = desc self.ext = ext self.sound = sound content = { 'Data': data, # 自定义消息数据,不作为APNS的payload中字段下发,故从payload中无法获取Data字段, String 'Desc': desc, # 自定义消息描述,当接收方为iphone后台在线时,做ios离线Push时文本展示 'Ext': ext, # 扩展字段,当接收方为ios系统且应用处在后台时,此字段作为APNS请求包Payloads中的ext键值下发,Ext的协议格式由业务方确定,APNS只做透传 'Sound': sound # 自定义APNS推送铃声 } super(CustomMsg, self).__init__(msgtype=msgtype, msgcontent=content) def set_data(self, data): self.data = data self.msgcontent['Data'] = data def set_desc(self, desc): self.desc = desc self.msgcontent['Desc'] = desc def set_ext(self, ext): self.ext = ext self.msgcontent['Ext'] = ext def set_sound(self, sound): self.sound = sound self.msgcontent['Sound'] = sound class SoundMsg(MsgDict): def __init__(self, uuid='', size=0, second=0, msgtype='TIMSoundElem'): self.uuid = uuid self.size = size self.second = second content = { 'UUID': uuid, # 语音序列号,后台用于索引语音的键值,String 'Size': size, # 语音数据大小, Number 'Second': second # 语音时长,单位秒 Number } super(SoundMsg, self).__init__(msgtype=msgtype, msgcontent=content) def set_uuid(self, uuid): self.uuid = uuid self.msgcontent['UUID'] = uuid def set_size(self, size): self.size = size self.msgcontent['Size'] = size def set_second(self, second): self.second = second self.msgcontent['Second'] = second class ImageMsg(MsgDict): def __init__(self, uuid='', imgformat=0, imginfo=[], msgtype='TIMImageElem'): self.uuid = uuid self.imgformat = imgformat self.imginfo = imginfo content = { 'UUID': uuid, # 图片序列号,后台用于索引语音的键值,String 'ImageFormat': imgformat, # 图片格式, BMP=1, JPG=2, GIF=3, 其他=0, Number 'ImageInfoArray': [t.info for t in imginfo] # 原图,缩略图或者大图下载信息, Array } super(ImageMsg, self).__init__(msgtype=msgtype, msgcontent=content) def set_uuid(self, uuid): self.uuid = uuid self.msgcontent['UUID'] = uuid def set_format(self, imgformat): self.imgformat = imgformat self.msgcontent['ImageFormat'] = imgformat def append_info(self, info): # info 为ImageInfo对象实例 self.imginfo.append(info) self.msgcontnet['ImageInfoArray'].append(info.info) def insert_info(self, index, info): self.imginfo.insert(index, info) self.msgcontent['ImageInfoArray'].insert(index, info.info) def del_info(self, index): del self.imginfo[index] del self.msgcontent['ImageInfoArray'][index] class FileMsg(MsgDict): def __init__(self, uuid='', size=0, name='', msgtype='TIMFileElem'): self.uuid = uuid self.size = size self.name = name content = { 'UUID': uuid, # 文件序列号,后台用于索引语音的键值,String 'FileSize': size, # 文件数据大小, Number 'FileName': name # 文件名称/路径, String } super(FileMsg, self).__init__(msgtype=msgtype, msgcontent=content) def set_uuid(self, uuid): self.uuid = uuid self.msgcontent['UUID'] = UUID def set_size(self, size): self.size = size self.msgcontent['FileSize'] = size def set_name(self, name): self.name = name self.msgcontent['FileName'] = name class ImageInfo(object): def __init__(self, itype=1, size=0, width=0, height=0, url=''): # 图片类型, 1-原图, 2-大图, 3-缩略图, 0-其他 self.itype = itype # 图片数据大小,Number self.size = size # 图片宽度,Number self.width = width # 图片高度, Number self.height = height # 图片下载地址,String self.url = url @property def info(self): return { 'Type': self.itype, 'Size': self.size, 'Width': self.width, 'Height': self.height, 'URL': self.url } def set_type(self, itype): self.itype = itype def set_size(self, size): self.size = size def set_width(self, width): self.width = width def set_height(self, height): self.height = height def set_url(self, url): self.url = url
2.微信开发包,python实现wechat_sdk开发
from __future__ import unicode_literals import time from wechat_sdk.exceptions import NeedParamError from wechat_sdk.lib.crypto import BasicCrypto from wechat_sdk.lib.request import WechatRequest from wechat_sdk.utils import disable_urllib3_warning class WechatConf(object): """ WechatConf 配置类 该类将会存储所有和微信开发相关的配置信息, 同时也会维护配置信息的有效性. """ def __init__(self, **kwargs): """ :param kwargs: 配置信息字典, 可用字典 key 值及对应解释如下: 'token': 微信 Token 'appid': App ID 'appsecret': App Secret 'encrypt_mode': 加解密模式 ('normal': 明文模式, 'compatible': 兼容模式, 'safe': 安全模式(默认)) 'encoding_aes_key': EncodingAESKey 值 (传入此值必须保证同时传入 token, appid, 否则抛出异常) 'access_token_getfunc': access token 获取函数 (用于单机及分布式环境下, 具体格式参见文档) 'access_token_setfunc': access token 写入函数 (用于单机及分布式环境下, 具体格式参见文档) 'access_token_refreshfunc': access token 刷新函数 (用于单机及分布式环境下, 具体格式参见文档) 'access_token': 直接导入的 access token 值, 该值需要在上一次该类实例化之后手动进行缓存并在此处传入, 如果不 传入, 将会在需要时自动重新获取 (传入 access_token_getfunc 和 access_token_setfunc 函数 后将会自动忽略此处的传入值) 'access_token_expires_at': 直接导入的 access token 的过期日期, 该值需要在上一次该类实例化之后手动进行缓存 并在此处传入, 如果不传入, 将会在需要时自动重新获取 (传入 access_token_getfunc 和 access_token_setfunc 函数后将会自动忽略此处的传入值) 'jsapi_ticket_getfunc': jsapi ticket 获取函数 (用于单机及分布式环境下, 具体格式参见文档) 'jsapi_ticket_setfunc': jsapi ticket 写入函数 (用于单机及分布式环境下, 具体格式参见文档) 'jsapi_ticket_refreshfunc': jsapi ticket 刷新函数 (用于单机及分布式环境下, 具体格式参见文档) 'jsapi_ticket': 直接导入的 jsapi ticket 值, 该值需要在上一次该类实例化之后手动进行缓存并在此处传入, 如果不 传入, 将会在需要时自动重新获取 (传入 jsapi_ticket_getfunc 和 jsapi_ticket_setfunc 函数 后将会自动忽略此处的传入值) 'jsapi_ticket_expires_at': 直接导入的 jsapi ticket 的过期日期, 该值需要在上一次该类实例化之后手动进行缓存 并在此处传入, 如果不传入, 将会在需要时自动重新获取 (传入 jsapi_ticket_getfunc 和 jsapi_ticket_setfunc 函数后将会自动忽略此处的传入值) 'partnerid': 财付通商户身份标识, 支付权限专用 'partnerkey': 财付通商户权限密钥 Key, 支付权限专用 'paysignkey': 商户签名密钥 Key, 支付权限专用 'checkssl': 是否检查 SSL, 默认不检查 (False), 可避免 urllib3 的 InsecurePlatformWarning 警告 :return: """ self.__request = WechatRequest() if kwargs.get('checkssl') is not True: disable_urllib3_warning() # 可解决 InsecurePlatformWarning 警告 self.__token = kwargs.get('token') self.__appid = kwargs.get('appid') self.__appsecret = kwargs.get('appsecret') self.__encrypt_mode = kwargs.get('encrypt_mode', 'safe') self.__encoding_aes_key = kwargs.get('encoding_aes_key') self.__crypto = None self._update_crypto() self.__access_token_getfunc = kwargs.get('access_token_getfunc') self.__access_token_setfunc = kwargs.get('access_token_setfunc') self.__access_token_refreshfunc = kwargs.get('access_token_refreshfunc') self.__access_token = kwargs.get('access_token') self.__access_token_expires_at = kwargs.get('access_token_expires_at') self.__jsapi_ticket_getfunc = kwargs.get('jsapi_ticket_getfunc') self.__jsapi_ticket_setfunc = kwargs.get('jsapi_ticket_setfunc') self.__jsapi_ticket_refreshfunc = kwargs.get('jsapi_ticket_refreshfunc') self.__jsapi_ticket = kwargs.get('jsapi_ticket') self.__jsapi_ticket_expires_at = kwargs.get('jsapi_ticket_expires_at') self.__partnerid = kwargs.get('partnerid') self.__partnerkey = kwargs.get('partnerkey') self.__paysignkey = kwargs.get('paysignkey') @property def token(self): """ 获取当前 Token """ self._check_token() return self.__token @token.setter def token(self, token): """ 设置当前 Token """ self.__token = token self._update_crypto() # 改动 Token 需要重新更新 Crypto @property def appid(self): """ 获取当前 App ID """ return self.__appid @property def appsecret(self): """ 获取当前 App Secret """ return self.__appsecret def set_appid_appsecret(self, appid, appsecret): """ 设置当前 App ID 及 App Secret""" self.__appid = appid self.__appsecret = appsecret self._update_crypto() # 改动 App ID 后需要重新更新 Crypto @property def encoding_aes_key(self): """ 获取当前 EncodingAESKey """ return self.__encoding_aes_key @encoding_aes_key.setter def encoding_aes_key(self, encoding_aes_key): """ 设置当前 EncodingAESKey """ self.__encoding_aes_key = encoding_aes_key self._update_crypto() # 改动 EncodingAESKey 需要重新更新 Crypto @property def encrypt_mode(self): return self.__encrypt_mode @encrypt_mode.setter def encrypt_mode(self, encrypt_mode): """ 设置当前加密模式 """ self.__encrypt_mode = encrypt_mode self._update_crypto() @property def crypto(self): """ 获取当前 Crypto 实例 """ return self.__crypto @property def access_token(self): """ 获取当前 access token 值, 本方法会自行维护 access token 有效性 """ self._check_appid_appsecret() if callable(self.__access_token_getfunc): self.__access_token, self.__access_token_expires_at = self.__access_token_getfunc() if self.__access_token: now = time.time() if self.__access_token_expires_at - now > 60: return self.__access_token self.grant_access_token() # 从腾讯服务器获取 access token 并更新 return self.__access_token @property def jsapi_ticket(self): """ 获取当前 jsapi ticket 值, 本方法会自行维护 jsapi ticket 有效性 """ self._check_appid_appsecret() if callable(self.__jsapi_ticket_getfunc): self.__jsapi_ticket, self.__jsapi_ticket_expires_at = self.__jsapi_ticket_getfunc() if self.__jsapi_ticket: now = time.time() if self.__jsapi_ticket_expires_at - now > 60: return self.__jsapi_ticket self.grant_jsapi_ticket() # 从腾讯服务器获取 jsapi ticket 并更新 return self.__jsapi_ticket @property def partnerid(self): """ 获取当前财付通商户身份标识 """ return self.__partnerid @property def partnerkey(self): """ 获取当前财付通商户权限密钥 Key """ return self.__partnerkey @property def paysignkey(self): """ 获取商户签名密钥 Key """ return self.__paysignkey def grant_access_token(self): """ 获取 access token 并更新当前配置 :return: 返回的 JSON 数据包 (传入 access_token_refreshfunc 参数后返回 None) """ self._check_appid_appsecret() if callable(self.__access_token_refreshfunc): self.__access_token, self.__access_token_expires_at = self.__access_token_refreshfunc() return response_json = self.__request.get( url="https://api.weixin.qq.com/cgi-bin/token", params={ "grant_type": "client_credential", "appid": self.__appid, "secret": self.__appsecret, }, access_token=self.__access_token ) self.__access_token = response_json['access_token'] self.__access_token_expires_at = int(time.time()) + response_json['expires_in'] if callable(self.__access_token_setfunc): self.__access_token_setfunc(self.__access_token, self.__access_token_expires_at) return response_json def grant_jsapi_ticket(self): """ 获取 jsapi ticket 并更新当前配置 :return: 返回的 JSON 数据包 (传入 jsapi_ticket_refreshfunc 参数后返回 None) """ self._check_appid_appsecret() if callable(self.__jsapi_ticket_refreshfunc): self.__jsapi_ticket, self.__jsapi_ticket_expires_at = self.__jsapi_ticket_refreshfunc() return response_json = self.__request.get( url="https://api.weixin.qq.com/cgi-bin/ticket/getticket", params={ "type": "jsapi", }, access_token=self.access_token, ) self.__jsapi_ticket = response_json['ticket'] self.__jsapi_ticket_expires_at = int(time.time()) + response_json['expires_in'] if callable(self.__jsapi_ticket_setfunc): self.__jsapi_ticket_setfunc(self.__jsapi_ticket, self.__jsapi_ticket_expires_at) return response_json def get_access_token(self): """ 获取 Access Token 及 Access Token 过期日期, 仅供缓存使用, 如果希望得到原生的 Access Token 请求数据请使用 :func:`grant_token` **仅为兼容 v0.6.0 以前版本使用, 自行维护 access_token 请使用 access_token_setfunc 和 access_token_getfunc 进行操作** :return: dict 对象, key 包括 `access_token` 及 `access_token_expires_at` """ self._check_appid_appsecret() return { 'access_token': self.access_token, 'access_token_expires_at': self.__access_token_expires_at, } def get_jsapi_ticket(self): """ 获取 Jsapi Ticket 及 Jsapi Ticket 过期日期, 仅供缓存使用, 如果希望得到原生的 Jsapi Ticket 请求数据请使用 :func:`grant_jsapi_ticket` **仅为兼容 v0.6.0 以前版本使用, 自行维护 jsapi_ticket 请使用 jsapi_ticket_setfunc 和 jsapi_ticket_getfunc 进行操作** :return: dict 对象, key 包括 `jsapi_ticket` 及 `jsapi_ticket_expires_at` """ self._check_appid_appsecret() return { 'jsapi_ticket': self.jsapi_ticket, 'jsapi_ticket_expires_at': self.__jsapi_ticket_expires_at, } def _check_token(self): """ 检查 Token 是否存在 :raises NeedParamError: Token 参数没有在初始化的时候提供 """ if not self.__token: raise NeedParamError('Please provide Token parameter in the construction of class.') def _check_appid_appsecret(self): """ 检查 AppID 和 AppSecret 是否存在 :raises NeedParamError: AppID 或 AppSecret 参数没有在初始化的时候完整提供 """ if not self.__appid or not self.__appsecret: raise NeedParamError('Please provide app_id and app_secret parameters in the construction of class.') def _update_crypto(self): """ 根据当前配置内容更新 Crypto 类 """ if self.__encrypt_mode in ['compatible', 'safe'] and self.__encoding_aes_key is not None: if self.__token is None or self.__appid is None: raise NeedParamError('Please provide token and appid parameters in the construction of class.') self.__crypto = BasicCrypto(self.__token, self.__encoding_aes_key, self.__appid) else: self.__crypto = None
版权:本文版权归作者
转载:欢迎转载,但未经作者同意,必须保留此段声明;必须在文章中给出原文连接;否则必究法律责任