ThinkChat🤖让你学习和工作更高效,注册即送10W Token,即刻开启你的AI之旅 广告
# 对象 [TOC] ## 访问控制(私有属性和私有方法) ```python # 私有属性(变量) class Student(object): def __init__(self, name, score): self.__name = name # 属性的名称前加上两个下划线__,外部就不能访问 self.__score = score def _show_info(self): print(self.__name) print(self.__score) def print_score(self): print('%s: %s' % (self.__name, self.__score)) bart = Student('Bart Simpson', 59) bart.__name # AttributeError: 'Student' object has no attribute '__name' bart.print_score() # Bart Simpson: 59 bart.__name = 'New Name' # 设置__name变量! bart.__name # 'New Name' # 注意: 看上去好像设置私有变量成功,但实际上这个__name变量和class内部的__name变量不是一个变量!内部的__name变量已经被Python解释器自动改成了_Student__name,而外部代码给bart新增了一个__name变量 bart.__show_info() # 私有方法不能访问:AttributeError: 'Student' object has no attribute '__show_info' ``` 1. 变量名类似__xxx__的,也就是以双下划线开头,并且以双下划线结尾的,是特殊变量,特殊变量是可以直接访问的,不是private变量 2. 只以一个下划线开头的实例变量名,比如_name,这样的实例变量外部是可以访问的,但是,按照约定俗成的规定,当你看到这样的变量时,意思就是,“虽然我可以被访问,但是,请把我视为私有变量,不要随意访问”。 ## 类的方法 ```python class Foo: name = 'who' count = 0 # __init__也属于实例方法,实例方法必须依托于实例 def __init__(self, name): self.name = name # 每实例一次,调用一次, 这里可以统计该类调用的次数 self.__class__.count += 1 print(self.__class__.count) # 类实例方法:第一个参数强制为类实例对象,一般用self代替 # 类实力为对象后可以直接访问的方法 # 可以通过这个类实例对象(self)访问对象属性 # 可以通过类实例对象(self)的__class__属性访问类属性 def hi(self , str): print(self.__class__.name) # who print(str , self.name) # hello zxg # 类方法:cls即为类自身 @classmethod def class_method(cls): print(cls.count) # 静态方法 # 不能传递和类或实例相关的参数,如cls或self,但可以传递其他参数 @staticmethod def static_method(nickname): print(nickname) if __name__ == '__main__': foo01 = Foo('zxg') # foo01.hi('hello') # Foo.hi('hello') # 报错:实例方法不能通过类直接用,必须依托于实例,当然可以把foo01的对象作为第一参数传进去 foo01.class_method() # 可以通过实例调用 Foo.class_method() # 也可以直接通过类来调用 # foo01.static_method('zxg') # Foo.static_method('zxg') # print(foo01) # print(Foo) ``` 1. 逻辑上,类方法应当只被类调用,实例方法实例调用,静态方法两者都能调用 2. 主要区别在于参数传递上的区别,实例方法悄悄传递的是self引用作为参数,而类方法悄悄传递的是cls引用作为参数。而静态方法不会任何隐式传递 ## 使用__slots__限制实例的属性 ```python class Student(object): __slots__ = ('name', 'age') # 用tuple定义允许绑定的属性名称 # 使用__slots__要注意,__slots__定义的属性仅对当前类实例起作用,对继承的子类是不起作用的: class GraduateStudent(Student): pass bart = Student() # 对象实例不需要用new ``` ## 类和类方法的装饰器 ### 类方法中的装饰器 ```python # 原来的class class Student(object): def get_score(self): return self._score def set_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 class Student(object): @property def score(self): return self._score @score.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 s = Student() s.score = 'haha' # OK,实际转化为s.set_score(60) s.score # OK,实际转化为s.get_score() # 只定义getter方法,不定义setter方法就是一个只读属性 class Student(object): @property def birth(self): return self._birth @birth.setter def birth(self, value): self._birth = value @property def age(self): return 2018 - self._birth zxg = Student() zxg.birth = 1988 zxg.age # 30 Student.sex = 1 zxg.sex = 1 # python一切皆对象,类也属于对象,可以动态为类添加属性,该属性会体现在实例后的对象里面 ``` ### 类装饰器 #### 函数装饰类 ##### 装饰器无参数 ```python #给每个类打印一句话 def Decorator(obj): print("定义了一个装饰器函数") return obj # 相当于执行 Decorator(School) @Decorator class School(): pass def godme(fun): def __godme(self,message): print('before') fun(self,message) print('after') return __godme class Person: def show(self,message): print(message) @godme def say(self,message): print(message) person = Person() # 相当于 godme(person.say('happy')) # 调用顺序 godme() -> __godme()-> person.say() # 输出顺序 before -> happy -> after person.say('happy') ``` ##### 装饰器有参数 ```python #给每个类添加一个可变的数据属性 def Decorator(**kwargs): def add(obj): # <class '__main__.School'> print(obj) "添加数据属性" print('调用外部函数的**kwargs',kwargs) for key,val in kwargs.items(): # 添加数据属性 setattr(obj,key,val) return obj print("外部传入的参数为:",kwargs) return add # 执行顺序: # 1.运行Decorator函数,先打印外部的传入的参数,返回add函数名; # 2.再执行School = add(School) # Decorator(addr = "浙江省杭州市",name ="浙江大学")(School) @Decorator(addr = "浙江省杭州市",name ="浙江大学") class School(): def __init__(self,price): self.price =price ``` #### 类装饰函数或方法 ##### 无参数 ```python class tracer: def __init__(self,func): print('__init__ is called' ) self.calls = 0 self.func = func def __call__(self,*args): self.calls += 1 print(args) print('call %s to %s' %(self.calls, self.func.__name__)) self.func(*args) @tracer def spam(a, b, c): print(a + b + c) ''' 1. 相当于 tracer(spam(1,2,3)) 2. 调用顺序:__init__(spam) -> __call__() -> spam(a, b, c) ''' spam(1,2,3) ``` ##### 有参数 ```python class tracer: def __init__(self, *args): self.calls = 0 self.args = args print(args) def __call__(self, func): self.func = func def realfunc(*args): self.calls += 1 print('call %s to %s' %(self.calls, self.func.__name__)) self.func(*args) return realfunc # 相当于tracer("xxxx")(spam) # 调用顺序 __init__("xxxx") -> __call__(spam) -> realfunc(a, b, c) -> spam(a, b, c) @tracer("xxxx") def spam(a, b, c): print(a + b + c) spam(1,2,3) ``` ## 继承 ```python class Parent: def myMethod(self): print ('调用父类方法') class Child(Parent): def myMethod(self): # super().myMethod(); # Python3,如果super在类里面的话,可以省略里面的参数 print ('调用子类方法') c = Child() # 子类实例 c.myMethod() # 子类调用重写方法 super(Child,c).myMethod() #用子类对象调用父类已被覆盖的方法 ``` ## 多重继承 在设计类的继承关系时,通常,主线都是单一继承下来的,如果需要“混入”额外的功能,通过多重继承就可以实现。 ```python class Dog(Mammal, RunnableMixIn, CarnivorousMixIn): pass ``` ## 定制类(魔术方法) 类似PHP中的魔术方法 ```python # __str__ # 打印实例时调用 # __repr__ # console直接敲命令时调用 # object python的顶级父类 class Student(object): def __init__(self, name): self.name = name # print打印时触发 def __str__(self): return 'Student object (name: %s)' % self.name # 控制台输出时触发 __repr__ = __str__ # 在Python console直接敲命令时调用__repr__方法,又因为__repr__方法跟__str__的内容一样 zxg = Student('zxg') print(zxg) # Student object (name: zxg) # __iter__ 实现迭代 # __getitem__ list化之后取第几个元素及切片 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 > 100: # 退出循环的条件 raise StopIteration() return self.a # 返回下一个值 # list化之后切片触发 def __getitem__(self, n): print(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 f = Fib() f[5] # 5 f[:5] # slice(1, 5, None) for n in f: print(n) # __setitem__() # 设置元素时触发 # __delitem__() # 删除元素时触发 # 通过以上的几个魔术方法,可以把自己定义的类表现出list,dict,tuple的特性 # __getattr__ 获取不存在的元素时调用 class Chain(object): def __init__(self, path=''): self._path = path def __getattr__(self, path): return Chain('%s/%s' % (self._path, path)) def __str__(self): return self._path __repr__ = __str__ Chain().status.user.list # /status/user/list 实现链式操作 # __call__ # 对实例直接调用 class Student(object): def __init__(self, name): self.name = name # 对实例直接调用的时候,调用的就是这个方法 def __call__(self): print('My name is %s.' % self.name) s = Student('Michael') s() # 直接可以调用 # __new__ 当你继承一些不可变的class时(比如int, str, tuple), 提供给你一个自定义这些类的实例化过程的途径。还有就是实现自定义的metaclass。 ``` ## 待处理 __del__ __enter__ __exit__ ## 使用枚举类 ```python from enum import Enum Month = Enum('Month', ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec')) print(Month) # <enum 'Month'> for name, member in Month.__members__.items(): # Sep => Month.Sep , 9 print(name, '=>', member, ',', member.value) ``` ## __new__ 和 __init__ > **实际上在我们实例化对象时,python默认执行了__new__操作,先创建类实例,然后返回给__init__初始化实例** ```python # python新类允许用户重载__new__和__init__方法。__new__创建类实例,__init__初始化一个已被创建的实例; class newStyleClass(object): # __new__在顶级父类object是一个静态方法 # cls代表当前类 def __new__(cls): print("__new__ is called") return super(newStyleClass, cls).__new__(cls) # self def __init__(self): print("__init__ is called") print("self is: ", self) newStyleClass() # 我们发现__new__函数先被调用,接着__init__函数在__new__函数返回一个实例的时候被调用,并且这个实例作为self参数被传入了__init__函数 # 若__new__()没有正确返回当前类cls的实例,那__init__()将不会被调用,即使是父类的实例也不行。 obj = 12 # obj can be an object from any class, even object.__new__(object) class returnExistedObj(object): def __new__(cls): print("__new__ is called") return obj # __init__不被调用 # return super(returnExistedObj,cls).__new__(cls) def __init__(self): print("__init__ is called") print("self is: ", self) returnExistedObj() ``` ## 使用元类 ### 通过type函数临时创建类 Python函数和类的定义,不是编译时定义的,而是运行时动态创建的;所以可以在运行时临时动态的创建类 `type`传入三个参数: 1. class的名称; 1. 继承的父类集合,注意Python支持多重继承,如果只有一个父类,别忘了tuple的单元素写法; 1. class的方法名称与函数绑定,这里我们把函数fn绑定到方法名hello上。这里的dict的参数是**kws; ```python def fn(self, name='world'): # 先定义函数 print('Hello, %s.' % name) Hello = type('Hello', (object,), dict(hello=fn)) # 创建Hello class o = Hello() o.hello() ``` ### metaclass 除了使用type()动态创建类以外,要控制类的创建行为,还可以使用metaclass。先定义metaclass,就可以创建类,最后创建实例。 metaclass是Python面向对象里最难理解,也是最难使用的魔术代码。正常情况下,你不会碰到需要使用metaclass的情况 ```python # metaclass是类的模板(类的类),所以必须从`type`类型派生: # metaclass的类名总是以Metaclass结尾 class ListMetaclass(type): def __new__(cls, name, bases, attrs): attrs['add'] = lambda self, value: self.append(value) return type.__new__(cls, name, bases, attrs) # 有了ListMetaclass,我们在定义类的时候还要指示使用ListMetaclass来定制类,传入关键字参数metaclass # 当我们传入关键字参数metaclass时,魔术就生效了,它指示Python解释器在创建MyList时,要通过ListMetaclass.__new__()来创建,在此,我们可以修改类的定义,比如,加上新的方法,然后,返回修改后的定义。 class MyList(list, metaclass=ListMetaclass): pass L = MyList() L.add(1) print(L) ``` **元类的作用** 1. 拦截类的创建; 2. 修改类; 3. 返回修改之后的类; ```python # 看下面这个例子 # type其实也是个元类 class UpperAttrMetaclass(type): def __new__(cls, clsname, bases, dct): uppercase_attr = {} for name, val in dct.items(): if not name.startswith('__'): uppercase_attr[name.upper()] = val else: uppercase_attr[name] = val return super(UpperAttrMetaclass, cls).__new__(cls, clsname, bases, uppercase_attr) # 我们知道在python中一切皆对象 # 那么下面我们的s其实就是一个类对象,它的__name__其实和Foo的__name__是一样的 # 看下面的方法是不是和type创建类有点像? s = UpperAttrMetaclass('Foo',(object,),{'bar':'bip'}) print(hasattr(s,'bar')) # False print(hasattr(s,'BAR')) # True print(s.BAR) # bip class Foo(object,metaclass=UpperAttrMetaclass): bar='bip' print(hasattr(Foo,'bar')) print(hasattr(Foo,'BAR')) print(Foo.BAR) # 最后它们输出的结果其实是一模一样的,这就说明了类其实和我们普通的实例对象差不多,只不过普通实例是通过类创建,而类是通过元类创建 # 而__new__就是用来创建实例的,不论是普通的实例,还是类实例,总之就是个实例 ``` **意义** 实现面向对象高级编程,体现其“开闭原则”:把扩展打开,把修改关闭。 比如: 编写一个ORM框架,所有的类都只能动态定义,因为只有使用者才能根据表的结构定义出对应的类来。 ```python 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().__init__(name, 'varchar(100)') # Python3里面的super可以省略StringField, self super(StringField, self).__init__(name, 'varchar(100)') # 字段名和字段类型 class IntegerField(Field): def __init__(self, name): super(IntegerField, self).__init__(name, 'bigint') # 字段名和字段类型 class ModelMetaclass(type): def __new__(cls, name, bases, attrs): # cls: 正在被生成的对象, name: 生成该对象的类名, bases: 父类集合, attrs: 属性方法集合 if name == 'Model': # 若类名为Model, 则是对Model的修改,这里是原样返回 return type.__new__(cls, name, bases, attrs) print('Found model: %s' % name) # 输出类的名字 mappings = dict() # 初始化user类的属性方法字典 for key, value in attrs.items(): if isinstance(value, Field): print('Found mappings: %s ==> %s' % (key, value)) # 输出属性类型和属性名 mappings[key] = value # 将属性类型(key)和属性名(value)加入到字典 for key in mappings.keys(): attrs.pop(key) # 类属性中删除该Field属性, 否则容易造成运行时错误(实例的属性会遮盖类的同名属性) attrs['__mappings__'] = mappings # 将属性方法字典加入到属性方法集合中 attrs['__table__'] = name # 表名(此处假设类名即为表名) return type.__new__(cls, name, bases, attrs) class Model(dict, metaclass=ModelMetaclass): # 使用ModelMetaclass来创建类 def __init__(self, **kw): # **kw命名关键词参数 print(kw) super(Model, self).__init__(**kw) def __getattr__(self, key): # 获得values 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): # 将属性保存到数据库(此处仅为生成相应的SQL语句) fields = [] params = [] args = [] for k, v in self.__mappings__.items(): fields.append(v.name) # 将字段名加入到fields中 params.append('?') # 向params中添加xxx个问号 args.append(getattr(self, k, None)) # 将values加入args中 sql = 'insert into %s (%s) values (%s)' % (self.__table__, ','.join(fields), ','.join(params)) # 生成SQL语句, join方法是将list中的内容以前面的符号作为分隔连在一起 print('SQL: %s' % sql) print('ARGS: %s' % args) class User(Model): # 定义类的属性到列的映射: id = IntegerField('id') name = StringField('username') email = StringField('email') password = StringField('password') # 创建一个实例: u = User(id=12345, name='Michael', email='test@orm.org', password='my-pwd') # 保存到数据库: u.save() # 过程是 ''' 从上到下执行: Model => ModelMetaclass => User(以及里面的属性IntegerField,StringField等) => ModelMetaclass => Model.save ''' ``` ### `__metaclass__` python2继承元类的语法 ```python # python3 class Model(dict, metaclass=ModelMetaclass): pass # python2 class Model(dict): __metaclass__ = ModelMetaclass pass ```