1、深入理解一切接对象:
1.1 什么是类和对象?
首先明白元类之前要明白什么叫做类。类是面向对象object oriented programming的重要概念。在面向对象中类和对象是最基本的两个概念。正如中国的道家所言,一生二,二生三,三生万物。类和对象正如这个阴阳的二元世界观,相辅相成存在的。
类英文class、对象英文instance。类是描述如何创建一个对象的代码段,用来描述具有相同的属性和方法的对象的集合,它定义了该集合中每个对象所共有的属性和方法。
因此我们有这样的概念。
类是对象的载体,对象又分属性和方法两个方面。
属性是对象的名词部分、方法是对象的动词部分。
1.2 一切接对象?
类是来自元初的,也就是在Py中类也是对象,也可以具有属性和方法两个方面。
1.3 这是一个哲学概念。
面向对象的最基本的问题是建立在哲学的思考上面的(当然我们用道家的思想来解释,老外可能也不是这么想的 ),因此我们有这样一个图示:
是不是面向对象的底层是哲学的观点。元类是不可再分的类,也就是类的加工厂,我们平时所建立的类其实是人家已经给你建立好的“工厂”、“模板”拿来可以直接用。当然我们也可以自定义一个“工厂”、“模板”,属于定制性的类。当然自定义一个类也好,使用人家Python给你现成的模板也罢。只要一个类一旦下生,就具备了属性和方法两个功能(人生下来就会吃饭和喘气),而且生下来的这个类本身也被大自然或者上帝看做是一个对象。
我们知道类的继承,在Python中type函数可以表示一个对象的属性,而且还可以创建一个类,也叫做类的加工厂。我们自定义一个类,让他们分别继承在父类(object)和元类(type),用dir命令来看看他们所具有的的属性和方法。
class Users1(object): pass class Users2(type): pass print(dir(Users1)) # [‘__class__‘, ‘__delattr__‘, ‘__dict__‘, ‘__dir__‘, ‘__doc__‘, ‘__eq__‘, ‘__format__‘, ‘__ge__‘, ‘__getattribute__‘, ‘__gt__‘, ‘__hash__‘, ‘__init__‘, ‘__init_subclass__‘, ‘__le__‘, ‘__lt__‘, ‘__module__‘, ‘__ne__‘, ‘__new__‘, ‘__reduce__‘, ‘__reduce_ex__‘, ‘__repr__‘, ‘__setattr__‘, ‘__sizeof__‘, ‘__str__‘, ‘__subclasshook__‘, ‘__weakref__‘] print(dir(Users2)) # [‘__abstractmethods__‘, ‘__base__‘, ‘__bases__‘, ‘__basicsize__‘, ‘__call__‘, ‘__class__‘, ‘__delattr__‘, ‘__dict__‘, ‘__dictoffset__‘, ‘__dir__‘, ‘__doc__‘, ‘__eq__‘, ‘__flags__‘, ‘__format__‘, ‘__ge__‘, ‘__getattribute__‘, ‘__gt__‘, ‘__hash__‘, ‘__init__‘, ‘__init_subclass__‘, ‘__instancecheck__‘, ‘__itemsize__‘, ‘__le__‘, ‘__lt__‘, ‘__module__‘, ‘__mro__‘, ‘__name__‘, ‘__ne__‘, ‘__new__‘, ‘__prepare__‘, ‘__qualname__‘, ‘__reduce__‘, ‘__reduce_ex__‘, ‘__repr__‘, ‘__setattr__‘, ‘__sizeof__‘, ‘__str__‘, ‘__subclasscheck__‘, ‘__subclasses__‘, ‘__subclasshook__‘, ‘__text_signature__‘, ‘__weakrefoffset__‘, ‘mro‘]
我们发现从元类继承过来的这个类,不光具有父类继承的一些属性和方法,而且还增加了好多方法。在我们实际使用的过程中,我们大多数是在父类这个层面上面来使用的。其中关于元类的很多属性和方法都被隐藏了。
print(Users1.__base__) print(Users2.__base__) # <class ‘object‘> # <class ‘type‘> print(Users1.__bases__) print(Users2.__bases__) # (<class ‘object‘>,) # (<class ‘type‘>,)
我们分别打印里面的属性,就可以看到其实User1也有这属性和方法,但是都被隐藏了。
2.property动态属性:
第一个问题:为什么要有动态属性?
提出动态属性,肯定就有静态属性这么一说。静态属性是在写类的时候通过构造函数或者类属性都之前定义好的。比如说。
例子1:构造函数定义好一些属性,平时就不太用更改了。
class Person: def __init__(self,name,age): self.name = name self.age = age if __name__ == ‘__main__‘: p = Person(‘thomas‘,18) print(p.age)
例子2:类属性定义。
class Person: human = ‘white human‘ def __init__(self,name,age): self.name = name self.age = age if __name__ == ‘__main__‘: p = Person(‘thomas‘,18) print(p.human)
这两个都叫做静态属性。
第二个问题:为什么要分动态属性和静态属性?
其实这是一种规范而已。举个例子:比如说我们有一个人的类的属性。大类来说分:“白种人”、“黄种人”、“黑人”等等,这些属性都是显而易见基本上都不会变化的属性,他们大多数都是用于分类而用的,在编程中类似于这类的属性归入静态属性的范畴(类的特性就是归类,要学会类必须要学会归类);另外,我们在人的这个类中,姓名、年龄、身高等等,每个人的个体变化都不一样,都是纷繁复杂的变化,我们把这类属性归入动态属性。
第三:写一个比较规范的,用构造函数改写的人的属性(动态属性和静态属性,用property装饰器的方式)
class Person: def __init__(self,race,national): self.race = race self.national = national @property def name(self): return self._name @name.setter def name(self,value): self._name = value @property def age(self): return self._age @age.setter def age(self,value): self._age = value @property def height(self): return self._height @height.setter def height(self,value): self._height = value if __name__ == ‘__main__‘: p = Person(‘yellow‘,‘China‘) # 打印静态属性 print(p.race,p.national) # 赋值动态属性 p.name = "Thomas" p.age = 38 p.height = 183 # 打印动态属性 print(p.name) print(p.height) print(p.age) # yellow China # Thomas # 183 # 38
一个重要的注意:!
@property def name(self): return self.name @name.setter def name(self,value): self.name = value
RecursionError: maximum recursion depth exceeded
·发现没有,如果我们将装饰器函数中的下划线去掉了。会出现了最大递归深度的报错。
重点:在用动态属性property的时候(其实大多数时候也带注意),方法名和里面属性名一致的时候,类属性的下划线不能少,否则会报错。原因在于,如果不加下划线出现self.属性名时会调用类的getattr方法不断调用会陷入死循环。
3.__getattr__,__getattribute__
#getattr是在查找不到属性的时候调用
#getattribute是查没查到着都会调用这个属性
第一个问题:为什么要有__getattr__和__getattribute__?
在实际的工作中,我们如果选择一些属性的时候,误选了或者本身这个属性没有的话会报错(当然大家都是),为了完善整个类中属性的调用,给这些属性加上一些特别的提示,不用报错停止,而是返回一些提示。这就用到了这两种方法。
第一个:关于getattr的方法
例子1:打印一个不存在的属性
class Person: def __init__(self,race,national): self.race = race self.national = national if __name__ == ‘__main__‘: p = Person(‘yellow‘,‘China‘) # 打印不存在的属性 print(p.company) # AttributeError: ‘Person‘ object has no attribute ‘company‘
发现会报一个属性的错误。
例子2:我们完善一下这个属性的调用
class Person: def __init__(self,race,national): self.race = race self.national = national def __getattr__(self, item): print("no this attribution") if __name__ == ‘__main__‘: p = Person(‘yellow‘,‘China‘) # 打印不存在的属性 print(p.company) # no this attribution # None
发现是打印这个属性不存在的内容,并返回一个空值。
第二个:发现__getattr__后面的参数存在一个item的传参?这是什么?
这是一个关于字典的传参。因此我们在__getattr__后还可以加入一些限定条件,我们就用这个传参item为例子。
例子1:
class Person: def __init__(self,race,national,info={}): self.race = race self.national = national self.info = info def __getattr__(self, item): return self.info[item] if __name__ == ‘__main__‘: p = Person(‘yellow‘,‘China‘,{"company":"CCTV"}) # 打印不存在的属性 print(p.company)
我们限定了字典的键内容,可以实现直接访问值的方式。
第三:关于__getattribute__这个是比较全的,
class Person: def __init__(self,race,national,info={}): self.race = race self.national = national self.info = info def __getattr__(self, item): return self.info[item] def __getattribute__(self, item): print("yes") if __name__ == ‘__main__‘: p = Person(‘yellow‘,‘China‘,{"company":"CCTV"}) # 打印不存在的属性 print(p.company) yes None
也就是找没找的到都会打印。
4. __get__、__set__、__delete__属性查找过程:
我们通过get,set来所属于的属性是否和我们预先设计的一样。这两个函数其实就是起到了静态语言的一种作用。
例子1:我们判断给类的属性输入30这个值是否是一个int类型的。如果不是返回是错误一句话;
import numbers class IsInt: def __get__(self, instance, owner): return self.value def __set__(self, instance, value): if not isinstance(value,numbers.Integral): print("value is not Int") else: self.value = value def __delete__(self, instance): pass class Person: age = IsInt() if __name__ == ‘__main__‘: p = Person() p.age = 30 print(p.age) # 30 p.age = ‘abc‘ print(p.age) # value is not Int
原文:https://www.cnblogs.com/noah0532/p/10951898.html