#**********使用__slots__限制实例属性********** class Student(object): pass def set_age(self,age): self.age = age from types import MethodType s = Student() s.set_age = MethodType(set_age,s) #给实例绑定一个方法,只有本实例有这个方法,其他的实例没有 s.set_age(25) print(s.age) def set_score(self,score): #通常情况下,set_score方法是直接定义在class中,但动态绑定允许我们在程序运行的过程中动态给class加上功能,这在静态语言中很难实现 self.score = score Student.set_score = set_score #给class绑定方法,这样所有由Student创建的实例,都可以使用该方法 s1 = Student() s.set_score(100) print(s.score) # 100 s1.set_score(99) print(s1.score) # 99 #为了达到限制的目的,比如,只允许对Student实例添加name和age属性, # Python允许在定义class的时候,定义一个特殊的__slots__变量,来限制该class实例能添加的属性 class Student(object): __slots__ = ("name","age") #这样表示由Student创建的实例最多有"name" "age"两个属性 s = Student() s.name = "perfey" #绑定属性"name" s.age = "24" #绑定属性"age" s.score = 99 #报错了,AttributeError: 'Student' object has no attribute 'score' #使用__slots__要注意,__slots__定义的属性仅对当前类实例起作用,对继承的子类是不起作用的 #除非在子类中也定义__slots__,这样,子类实例允许定义的属性就是自身的__slots__加上父类的__slots__ class Student(object): def __init__(self,name): self.name = name __slots__ = "name" #父类只允许name属性 ,如果没有这里的__slot__,后面Student的实例可以通过 s.score = 80 这样的方式来增加属性 class Student_son(Student): __slots__ = "age" #子类这里允许的是 "age" 和 "name"属性,如果没有这里的__slot__,后面的Student_son的实例的属性没有限制 bart = Student_son("bart") bart.age = 18 print(bart.age) #18 bart.name = "afly" print(bart.name) #afly
#**************使用property装饰器检查实例传入参数属性************ class Student(object): pass s1 = Student() s1.score = 60 #直接使用属性 print(s1.score) class Student(object): def get_score(self): #通过get_score方法来得到分数这个值 return self._score def set_score(self, value): #通过set_score方法来检查实例参数,并传参 if not isinstance(value, int): raise ValueError('score must be an integer!') if value < 0 or value > 100: raise ValueError('score must between 0 ~ 100!') self._score = value #在这个位置绑定属性,传递参数 s1 = Student() s1.set_score(60) #调用方法 ret = s1.get_score() #调用方法 print(ret) class Student(object): @property #通过property装饰器使得可以用类似属性这样的简单方式访问类的变量,这是把一个getter方法变成属性 def score(self): return self._score @score.setter #把一个setter方法变成属性并赋值 def score(self, value): if not isinstance(value, int): raise ValueError('score must be an integer!') if value < 0 or value > 100: raise ValueError('score must between 0 ~ 100!') self._score = value s1 = Student() s1.score = 60 print(s1.score) class Student(object): @property def birth(self): return self._birth @birth.setter def birth(self, value): self._birth = value @property def age(self): #只定义getter方法,不定义setter方法就是一个只读属性 return 2015 - self._birth s1 = Student() s1.birth = 1995 print(s1.age)
#**************************多重继承************************* class Dog(Mammal, RunnableMixIn, CarnivorousMixIn): #一般书写习惯是;主线继承的类放在第一个,其他的次要的类以MinIn结尾 pass
class Student(object): def __init__(self, name): self.name = name def __str__(self): #通过__str__()方法,返回一个好看的字符串 return 'Student object (name=%s)' % self.name __repr__ = __str__ #直接显示变量(在python自带的shell编辑器中敲变量名会显示变量内容)调用的不是__str__(),而是__repr__(),两者的区别是__str__()返回用户看到的字符串,而__repr__()返回程序开发者看到的字符串,也就是说,__repr__()是为调试服务的。 #解决办法是再定义一个__repr__()。但是通常__str__()和__repr__()代码都是一样的,所以,有个偷懒的写法__repr__ = __str__ s = Student("alex") print(s) #Student object (name=alex) class Fib(object): def __init__(self): self.a, self.b = 0, 1 # 初始化两个计数器a,b def __iter__(self): return self # 实例本身就是迭代对象,故返回自己 def __next__(self): self.a, self.b = self.b, self.a + self.b # 计算下一个值 if self.a > 100000: # 退出循环的条件 raise StopIteration() return self.a # 返回下一个值 for n in Fib(): print(n) #*****__getitem__定义迭代器类,可以进行索引************ class Fib(object): def __getitem__(self, n): a, b = 1, 1 for x in range(n): a, b = b, a + b if a > 100000: # 退出循环的条件 raise StopIteration() return a f = Fib() print(f[0]) for n in Fib(): print(n) #为了让我们定义的迭代器具有和list一样的可进行切片操作,定义可切片的类 ,这里我们没有对步长和索引为负数进行处理 class Fib(object): def __getitem__(self, n): if isinstance(n, int): # n是索引 a, b = 1, 1 for x in range(n): a, b = b, a + b return a if isinstance(n, slice): # n是切片 start = n.start stop = n.stop if start is None: start = 0 a, b = 1, 1 L = [] for x in range(stop): if x >= start: L.append(a) a, b = b, a + b return L #*************__getattr__()方法,动态返回一个属性*********** class Student(object): def __getattr__(self, attr): if attr=='age': return lambda: 25 raise AttributeError('\'Student\' object has no attribute \'%s\'' % attr) # attr 就是动态属性,使用没有定义的属性时,attr保存改属性的名称 # 当在__getattr__中仍未找到属性时,默认返回None.要让class只响应特定的几个属性,我们就要按照约定,抛出AttributeError的错误 s = Student() ret = s.age() print(ret) class Chain(object): def __init__(self, path=''): self._path = path def __getattr__(self, path): return Chain('%s/%s' % (self._path, path)) #把实例的参数和未定义的方法作为新的参数传递给Chain类,并根据__str__返回 def __str__(self): return self._path __repr__ = __str__ c1 = Chain("/d/s") c2 = Chain().status.user.timeline.list c3 = Chain("/d/s").status.user.timeline.list print(c1) #/d/s print(c2) #/status/user/timeline/list print(c3) #/d/s/status/user/timeline/list class Student(object): def __init__(self, name): self.name = name def __call__(self): print('My name is %s.' % self.name) s = Student("alex") s.__call__() #调用实例方法 #My name is alex s() #通过__call__方法,直接对实例本身调用等价于调用__call__方法 #My name is alex. """__call__()还可以定义参数。对实例进行直接调用就好比对一个函数进行调用一样,所以你完全可以把对象看成函数,把函数看成对象,因为这两者之间本来就没啥根本的区别。 如果你把对象看成函数,那么函数本身其实也可以在运行期动态创建出来,因为类的实例都是运行期创建出来的,这么一来,我们就模糊了对象和函数的界限。 那么,怎么判断一个变量是对象还是函数呢?其实,更多的时候,我们需要判断一个对象是否能被调用,能被调用的对象就是一个Callable对象,比如函数和我们上面定义的带有__call__()的类实例 通过callable()函数,我们就可以判断一个对象是否是“可调用”对象""" print(callable(Student)) #True print(callable(s)) #True print(callable(max)) #True print(callable("abc")) #False
# ============================================= # 网上的分析,挺详细的 # 完全动态调用特性: # 把一个类的所有属性和方法调用全部动态化处理 # __call__(): 用于实例自身的调用,达到()调用的效果 # 即可以把此类的对象当作函数来使用,相当于重载了括号运算符 # __getattr__(): 当调用不存在的属性时调用此方法来尝试获得属性 class Chain(object): def __init__(self, path=''): # 默认路径参数path为空 self._path = path def __getattr__(self, path): print('call __getattr__(%s)' % path) return Chain('%s/%s' % (self._path, path)) #注意两个 %s 中间有 / def __str__(self): return self._path def __call__(self, param): print('cal __call__(%s)' % param) return Chain('%s/%s' % (self._path, param)) __repr__ = __str__ # /status/user/timeline/list # Chain().status.user.timeline.list调用分析 # 首先执行Chain()返回一个实例对象C1(path = ''), # 通过实例对象C1来获取status属性,因为C1中不存在status属性,所以就会调用 # __getattr__()来尝试获取status属性,接着通过__getattr__()方法返回 # 带参数status的实例对象C2(path = '/status'),然后通过实例对象C2来获取user属性, # C2中不存在user属性,接着调用__getattr__()方法返回带参数user # 的实例对象C3(path = '/status/user'),然后通过实例对象C3来获取timeline属性, # 因C3不存在timeline属性,故调用__getattr__()方法返回带参数timeline # 的实例对象C4(path = '/status/user/timeline'),通过实例对象C4来获取list属性, # 又因C4中不存在list属性,调用__getattr__()方法返回带参数list # 的实例对象C5(path = '/status/user/timeline/list'), # 最后通过调用__str__()方法来打印实例对象C5,即返回/status/user/timeline/list # 具体参考见下面的测试结果 print(Chain().status.user.timeline.list) print('--------------------------------------') # GET /users/:user/repos # :user替换为实际用户名 # /users/Lollipop/repos # Chain().users('Lollipop').repos 调用分析 # 首先执行Chain()返回一个实例对象Q1(path = ''), # 通过实例对象Q1来获取users属性,因为Q1中不存在users属性, # 所以就会调用__getattr__()方法尝试获取users属性,接着通过 # __getattr__()方法返回带参数users的实例对象Q2(path = '/users'), # 然后因为通过()直接调用实例对象Q2(实例后边有括号相当于函数调用__call__方法),并带参数'Lollipop',故会调用 # __call__()方法,返回了带参数Lollipop的实例对象Q3(path = '/users/Lollipop'), # 接着通过实例对象Q3来获取repos属性,又因Q3中不存在repos属性,即会调用 # __getattr__()方法返回带参数repos的实例对象Q4(path = '/users/Lollipop/repos') # 最后通过调用__str__()方法来打印实例对象Q4,即返回/users/Lollipop/repos # 具体参考见下面的测试结果 print(Chain().users('Lollipop').repos) ''' # log analysis call __getattr__(status) call __getattr__(user) call __getattr__(timeline) call __getattr__(list) /status/user/timeline/list -------------------------------------- call __getattr__(users) cal __call__(Lollipop) call __getattr__(repos) /users/ollipop/repos '''
#Enum可以把一组相关常量定义在一个class中,且class不可变,而且成员可以直接比较 from enum import Enum Month1 = Enum("Month",("Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec")) # "Month"是类 ,"Jan"."Feb"....这些是由类创建的实例 for name,member in Month1.__members__.items(): print(name,"=>",member,",",member.value) #value属性是自动赋值给成员的int常量,默认从1开始计数 # # Jan => Month.Jan , 1 # Feb => Month.Feb , 2 # Mar => Month.Mar , 3 # Apr => Month.Apr , 4 # May => Month.May , 5 # Jun => Month.Jun , 6 # Jul => Month.Jul , 7 # Aug => Month.Aug , 8 # Sep => Month.Sep , 9 # Oct => Month.Oct , 10 # Nov => Month.Nov , 11 # Dec => Month.Dec , 12 from enum import Enum, unique @unique #@unique装饰器可以帮助我们检查,保证没有重复值 class Weekday(Enum): Sun = 0 # Sun的value被设定为0 Mon = 1 Tue = 2 Wed = 3 Thu = 4 Fri = 5 Sat = 6 print(Weekday.Mon) #Weekday.Mon print(Weekday["Tue"]) #Weekday.Tue print(Weekday(2)) #Weekday.Tue print(Weekday.Mon.value) #1 for name,member in Weekday.__members__.items(): print(name,"=>",member) # Sun => Weekday.Sun # Mon => Weekday.Mon # Tue => Weekday.Tue # Wed => Weekday.Wed # Thu => Weekday.Thu # Fri => Weekday.Fri # Sat => Weekday.Sat
#**********************通过type()函数动态创建类**************** def fn(self,name="world"): print("Hello,%s" % name) Hello = type("Hello",(object,),dict(hello=fn)) # type() 传入的三个参数,1.类的名称,2.继承的父类集合,python支持多重继承,只有一个父类时,注意tuple的单元素写法.3.class的方法名称和函数绑定,这里我们把函数fn绑定到方法名hello上 h = Hello() h.hello() #Hello,world
##*************************metaclass******************************* #metaclass可以创建或者修改类,可以把类看做是metaclass创建出来的实例 #使用metaclass的顺序是,先定义metaclass,就可以创建类,最后创建实例,metaclass的类名总是以Metaclass结尾,以便清楚地表示这是一个metaclass class ListMetaclass(type): def __new__(cls, name, bases, attrs): # __new__ 是继承 type的方法,可以创建新的类(或者叫做type的对象),返回类名 attrs['add'] = lambda self, value: self.append(value) # print(ListMetaclass.__new__(cls, name, bases, attrs)) # 相当于返回上面语句去执行 def __new__(cls, name, bases, attrs):这样会一直创建新的类,相当于递归,当创建的超过内存允许的最大值时就会报错 # print(type.__new__(cls, name, bases, attrs)) # <class '__main__.MyList'> # 这里的 cls = <class '__main__.ListMetaclass'> type类型 name = 'MyList' str类型 bases = <class 'tuple'>: (<class 'list'>,) tuple类型 attrs = {dict} {'__module__': '__main__', '__qualname__': 'MyList', 'put': <function MyList.put at 0x00000153008D49D8>} dict类型 return type.__new__(cls, name, bases, attrs) #这里是通过type返回的类名作为ListMetaclass __new__ 方法的返回值,不使用ListMetaclass.__new__(cls, name, bases, attrs)是因为在函数内部使用该函数相当于递归调用,会不断调用 class MyList(list, metaclass=ListMetaclass): #当我们传入关键字参数metaclass时,魔术就生效了,它指示Python解释器在创建MyList时,要通过ListMetaclass.__new__()来创建,在此,我们可以修改类的定义,比如,加上新的方法,然后,返回修改后的定义.__new__()方法接收到的参数依次是:1.当前准备创建的类的对象;2.类的名字 3.类继承的父类集合 4.类的方法集合 def put(self): print("我是对象") pass l1 = MyList()
#ORM:object relational mapping 对象-关系映射 #把关系数据库的一行映射为一个对象,也就是一个类对应一个表 #ORM框架所有的类只能动态定义 #定义一个User类来操作对应的数据库表User class Field(object):#保存数据库表的字段名和字段类型 def __init__(self,name,column_type): self.name=name self.column_type=column_type def __str__(self): return '<%s:%s>'%(self.__class__.__name__,self.name)#输出<对象所属的类名:对象的名字> class StringField(Field): def __init__(self,name): super(StringField,self).__init__(name,'varchar(100)') # 规定StringField的数据类型是varchar(100) class IntegerField(Field): def __init__(self, name): super(IntegerField, self).__init__(name, 'bigint') # 上面这三个函数相当于定义了一种新的数据类型,和 int float一样,可以使用StringField() 或者IntegerField()来创造这种数据类型的实例 class ModelMetaclass(type): def __new__(cls,name,bases,attrs):#当前metaclass方法准备创建的类的对象,类的名字,类继承的父类集合,类的方法集合.以下的操作都是对新创建的类的操作 if name=='Model': return type.__new__(cls,name,bases,attrs)#封锁对Model的修改,类名是Model就只能返回 print('Found model:%s'%name) mappings=dict()#新建一个字典 print(attrs.items()) #dict_items([('__module__', '__main__'), ('__qualname__', 'User'), ('id', <__main__.IntegerField object at 0x000001B5042CE860>), ('name', <__main__.StringField object at 0x000001B5042CE898>), ('email', <__main__.StringField object at 0x000001B5042CE8D0>), ('password', <__main__.StringField object at 0x000001B5042CE908>)]) for k,v in attrs.items(): print(k,v) #__module__ __main__ #__qualname__ User #id <IntegerField:id> #name <StringField:username> #email <StringField:email> #password <StringField:password> if isinstance(v, Field): print('Found mapping: %s ==> %s' % (k, v)) #v是属于Field类的,因此v遇到print会自动调用Field中的方法__str__ attr是无序的字典,因此打印输出的时候也是无序的 mappings[k] = v # 在当前类(比如User)中查找定义的类的所有属性,如果找到一个Field属性,就把它保存到一个__mappings__的dict中, # 同时从类属性中删除该Field属性,否则,容易造成运行时错误(实例的属性会遮盖类的同名属性); for k in mappings.keys(): attrs.pop(k) attrs['__mappings__'] = mappings # 保存属性和列的映射关系,这属于User标的属性 attrs['__table__'] = name # 假设表名和类名一致 return type.__new__(cls, name, bases, attrs)#属性修改完毕后返回 class Model(dict,metaclass=ModelMetaclass): def __init__(self,**kw): super(Model,self).__init__(**kw) def __getattr__(self,key):#实例.属性的时候自动调用 try: return self[key] except KeyError: raise AttributeError(r"'Model' object has no attribute'%s'"%key) def __setattr__(self,key,value): self[key]=value def save(self): fields=[] params=[] args=[] for k,v in self.__mappings__.items(): fields.append(v.name) #k 是列名,v是列名对应的Field数据,实际上是从id=IntegerField('id')出来的,v.name这里用的是Field的属性name params.append('?') args.append(getattr(self,k,None)) # 通过getattr方法使用字典找到 列名(id)的值 12345 sql='insert into %s(%s) values(%s)'%(self.__table__,','.join(fields),','.join(params)) print('SQL:%s'%sql) print('ARGS:%s'%str(args)) class User(Model): # 定义类的属性到列的映射 id=IntegerField('id')#用IntegerField初始化User类的属性,调用setattr自动赋值,和User继承的其他方法作为字典保存到ModelMetaclass中的attrs方法集合中 name=StringField('username') email=StringField('email') password=StringField('password') #创建一个实例 u=User(id=12345, name='Michael', email='test@orm.org', password='my-pwd') # 这些是对User类的属性的赋值 # User 继承了Model,Model继承了dict,所以可以把User具有dict的方法 #保存到数据库 u.save() #创建实例的时候调用Model的方法->调用ModelMetaclass的方法
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
#********通过继承实现接口*********** #声明某个子类兼容于某基类,定义一个接口类Interface,接口类中定义了一些接口名(就是函数名)且并未实现接口的功能,子类继承接口类,并且实现接口中的功能 from abc import ABCMeta,abstractmethod class Payment(metaclass=ABCMeta): # 抽象类 接口类 规范和约束 metaclass指定的是一个元类 @abstractmethod def pay(self):pass # 抽象方法 class Alipay(Payment): def pay(self,money): print('使用支付宝支付了%s元'%money) class QQpay(Payment): def pay(self,money): print('使用qq支付了%s元'%money) class Wechatpay(Payment): # def pay(self,money): # print('使用微信支付了%s元'%money) def recharge(self):pass def pay(a,money): a.pay(money) # 如果Wechatpay类中没有pay方法时,会调用父类Payment中的pay方法,但是Payment中的pay是抽象的不能实例化,所以会报错 a = Alipay() a.pay(100) pay(a,100) # 归一化设计:不管是哪一个类的对象,都调用同一个函数去完成相似的功能 q = QQpay() q.pay(100) pay(q,100) w = Wechatpay() pay(w,100) # 到用的时候才会报错,不能用抽象的方法实例化一个抽象的类Wechatpay()
#******普通方法.类方法和静态方法******** class Foo: def __init__(self, name): self.name = name def ord_func(self): """ 定义普通方法,至少有一个self参数 """ # print self.name print('普通方法') @classmethod def class_func(cls): # cls传入当前方法的类名,与self作用差不多,self传入对象名 """ 定义类方法,至少有一个cls参数 """ cls.name = "sylar" print('类方法') @staticmethod def static_func(): """ 定义静态方法 ,无默认参数""" print('静态方法') # 调用普通方法 f = Foo("alex") f.ord_func() # 普通方法 # 调用类方法 Foo.class_func() # 类方法 print(Foo.name) # sylar # 调用静态方法 Foo.static_func() # 静态方法 class People: def __init__(self,name,weight,height): self.name=name self.weight=weight self.height=height @property def bmi(self): return self.weight / (self.height**2) p1=People('egon',75,1.85) print(p1.bmi) # 使用了@property后可以把 bmi方法当做一个属性 bmi = self.weight / (self.height**2) 不必再使用 bmi() 执行函数 # print(p1.bmi()) # 如果没有@property 调用 bmi方法必须加括号 p1.bmi() 打印返回值 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, value): del self.original_price obj = Goods() print(obj.price) # 获取商品价格 obj.price = 200 # 修改商品原价 print(obj.price) del obj.price # 删除商品原价 print(obj.price) # 这时会报错,因为已经删除了original_price 属性 price() missing 1 required positional argument: 'value' import sys def s1(): print(s1) def s2(): print(s2) this_module = sys.modules[__name__] ret1 = hasattr(this_module, 's1') print(ret1) ret2 = getattr(this_module, 's2') print(ret2) 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 f1['age'] f1['name']='alex' print(f1.__dict__) object将__new__()方法定义为静态方法,并且至少需要传递一个参数cls,\ cls表示需要实例化的类,此参数在实例化时由Python解释器自动提供 class Demo(object): def __init__(self): print( '__init__() called...') def __new__(cls, *args, **kwargs): print( '__new__() - {cls}'.format(cls=cls)) return object.__new__(cls, *args, **kwargs) if __name__ == '__main__': de = Demo() print(de) # <__main__.Demo object at 0x000002252EFA8B70> print(Demo) # <class '__main__.Demo'> # __new__() - <class '__main__.Demo'> # __init__() called... """ 发现实例化对象的时候,调用__init__()初始化之前,先调用了__new__()方法 __new__()必须要有返回值,返回实例化出来的实例,\ 需要注意的是,可以return父类__new__()出来的实例,也可以直接将object的__new__()出来的实例返回。 __init__()有一个参数self,该self参数就是__new__()返回的实例,\ __init__()在__new__()的基础上可以完成一些其它初始化的动作,__init__()不需要返回值。 若__new__()没有正确返回当前类cls的实例,那__init__()将不会被调用,即使是父类的实例也不行。 我们可以将类比作制造商,__new__()方法就是前期的原材料购买环节,\ __init__()方法就是在有原材料的基础上,加工,初始化商品环节。 """ class Singleton(object): def __new__(cls, *args, **kwargs): if not hasattr(cls, '_instance'): cls._instance = super(Singleton, cls).__new__(cls, *args, **kwargs) return cls._instance # return object.__new__(cls,*args,**kwargs) s = Singleton() print(s._instance) # <__main__.Singleton object at 0x000001C737868978> print(s) # <__main__.Singleton object at 0x000001C737868978> print(s.__dict__) # {} print(Singleton.__dict__) #{'__module__': '__main__', '__new__': <staticmethod object at 0x000001C737868940>, \ # '__dict__': <attribute '__dict__' of 'Singleton' objects>, '__weakref__': <attribute '__weakref__' of 'Singleton' objects>,\ # '__doc__': None, '_instance': <__main__.Singleton object at 0x000001C737868978>}
class A: def __init__(self): self.x = 1 def __new__(cls, *args, **kwargs): print(cls) print("in new function") # return super().__new__(cls) return object.__new__(cls) a1 = A() # 在实例化的时候首先找类的__new__方法(没有时往上找object里有)\ # 执行__new__方法,如果__new__方法中没有创建对象空间则必须再执行父类中能创建\ # 对象空间的__new__方法(object的new方法就是专门创建对象的方法),索引通常在类的\ # __new__方法中返回 object.__new__(cls) 的值