类命名空间与对象、实例的命名空间
创建一个类就会创建一个类的名称空间,用来存储类中定义的所有名字,这些名字称为类的属性
而类有两种属性:静态属性和动态属性(静态属性就是直接在类中定义的变量)(动态属性就是定义在类中的方法)
其中类的数据属性是共享给所有对象的(主要指类内对象的内存地址是一致的)
软件重用的重要方式除了继承之外还有另外一种方式,即:组合(组合指的是,在一个类中以另外一个类的对象作为数据属性,称为类的组合)
例子1:公司,职员绑定
class Company: def __init__(self, name, address): self.name = name self.address = address class Department: def __init__(self, name, project): self.name = name self.project = project class Staff(Company, Department): def __init__(self, name, age, sex): self.name = name self.age = age self.sex = sex # 创建公司信息 haymaker = Company('haymaker', 'shanghai') # 创建部门信息 sa = Department('sa', 'The Walker') # 创建员工信息 userinfo = Staff('fred', '26', 'male') # 将部门和公司信息绑定给员工,生成员工信息 userinfo.company = haymaker userinfo.department = sa print(''' <姓名:%s 年龄:%s 性别:%s 公司:%s 公司地址:%s 部门:%s 负责产品:%s> ''' % (userinfo.name, userinfo.age, userinfo.sex, userinfo.company.name, userinfo.company.address, userinfo.department.name, userinfo.department.project)) print(userinfo.company.__dict__) print(userinfo.department.__dict__)
例子2:环形面积计算(组合)
from math import pi class Circle: ''' 定义了一个圆形类; 提供计算面积(area)和周长(perimeter)的方法 ''' def __init__(self, radius): self.radius = radius def area(self): return pi * self.radius * self.radius def perimeter(self): return 2 * pi * self.radius circle = Circle(10) # 实例化一个圆 area1 = circle.area() # 计算圆面积 per1 = circle.perimeter() # 计算圆周长 print(area1, per1) # 打印圆面积和周长 class Ring: ''' 定义了一个圆环类 提供圆环的面积和周长的方法 ''' def __init__(self, radius_outside, radius_inside): self.outsid_circle = Circle(radius_outside) self.inside_circle = Circle(radius_inside) def area(self): return self.outsid_circle.area() - self.inside_circle.area() def perimeter(self): return self.outsid_circle.perimeter() + self.inside_circle.perimeter() ring = Ring(10, 5) # 实例化一个环形 print(ring.perimeter()) # 计算环形的周长 print(ring.area()) # 计算环形的面积
当类之间有显著不同,并且较小的类是较大的类所需要的组件时,用组合比较好
总结的例子:人狗大战
class Person: # 定义一个人类 role = 'person' # 人的角色属性都是人 def __init__(self, name, aggressivity, life_value, money): self.name = name # 每一个角色都有自己的昵称; self.aggressivity = aggressivity # 每一个角色都有自己的攻击力; self.life_value = life_value # 每一个角色都有自己的生命值; self.money = money def attack(self, dog): # 人可以攻击狗,这里的狗也是一个对象。 # 人攻击狗,那么狗的生命值就会根据人的攻击力而下降 dog.life_value -= self.aggressivity class Dog: # 定义一个狗类 role = 'dog' # 狗的角色属性都是狗 def __init__(self, name, breed, aggressivity, life_value): self.name = name # 每一只狗都有自己的昵称; self.breed = breed # 每一只狗都有自己的品种; self.aggressivity = aggressivity # 每一只狗都有自己的攻击力; self.life_value = life_value # 每一只狗都有自己的生命值; def bite(self, people): # 狗可以咬人,这里的狗也是一个对象。 # 狗咬人,那么人的生命值就会根据狗的攻击力而下降 people.life_value -= self.aggressivity class Weapon: def __init__(self, name, price, aggrev, life_value): self.name = name self.price = price self.aggrev = aggrev self.life_value = life_value def update(self, obj): # obj就是要带这个装备的人 obj.money -= self.price # 用这个武器的人花钱买所以对应的钱要减少 obj.aggressivity += self.aggrev # 带上这个装备可以让人增加攻击 obj.life_value += self.life_value # 带上这个装备可以让人增加生命值 def prick(self, obj): # 这是该装备的主动技能,扎死对方 obj.life_value -= 500 # 假设攻击力是500 lance = Weapon('长矛', 200, 6, 100) egg = Person('egon', 10, 1000, 600) # 创造了一个实实在在的人egg ha2 = Dog('二愣子', '哈士奇', 10, 1000) # 创造了一只实实在在的狗ha2 # egg独自力战"二愣子"深感吃力,决定穷毕生积蓄买一把武器 if egg.money > lance.price: # 如果egg的钱比装备的价格多,可以买一把长矛 lance.update(egg) # egg花钱买了一个长矛防身,且自身属性得到了提高 egg.weapon = lance # egg装备上了长矛 print(egg.money, egg.life_value, egg.aggressivity) print(ha2.life_value) egg.attack(ha2) # egg打了ha2一下 print(ha2.life_value) egg.weapon.prick(ha2) # 发动武器技能 print(ha2.life_value) # ha2不敌狡猾的人类用武器取胜,血槽空了一半
面向对象的三大特性
什么是继承?继承是一种创建新类的方式,在python中,新建的类可以继承一个或多个父类,父类又可称为基类或超类,新建的类称为派生类或子类
class ParentClass1: #定义父类 pass class ParentClass2: #定义父类 pass class SubClass1(ParentClass1): #单继承,基类是ParentClass1,派生类是SubClass pass class SubClass2(ParentClass1,ParentClass2): #python支持多继承,用逗号分隔开多个继承的类 pass 提示:如果没有指定基类,python的类会默认继承object类,object是所有python类的基类,它提供了一些常见方法(如__str__)的实现。
继承与抽象(先抽象再继承)
抽象即抽取类似或者说比较像的部分。
抽象分成两个层次:
1.将奥巴马和梅西这俩对象比较像的部分抽取成类;
2.将人,猪,狗这三个类比较像的部分抽取成父类。
抽象最主要的作用是划分类别(可以隔离关注点,降低复杂度)
继承:是基于抽象的结果,通过编程语言去实现它,肯定是先经历抽象这个过程,才能通过继承的方式去表达出抽象的结构。
抽象只是分析和设计的过程中,一个动作或者说一种技巧,通过抽象可以得到类
#继承的代码实现 class Animal: def eat(self): print("%s 吃 " %self.name) def drink(self): print ("%s 喝 " %self.name) class Cat(Animal): def __init__(self, name): self.name = name self.breed = '猫' def climb(self): print('爬树') class Dog(Animal): def __init__(self, name): self.name = name self.breed='狗' def look_after_house(self): print('汪汪叫') # ######### 执行 ######### c1 = Cat('小白家的小黑猫') c1.eat() c2 = Cat('小黑的小白猫') c2.drink() d1 = Dog('胖子家的小瘦狗') d1.eat()
在开发程序的过程中,如果我们定义了一个类A,然后又想新建立另外一个类B,但是类B的大部分内容与类A的相同时,我们不可能从头开始写一个类B,这就用到了类的继承的概念。
通过继承的方式新建类B,让B继承A,B会‘遗传’A的所有属性(数据属性和函数属性),实现代码重用。
提示:用已经有的类建立一个新的类,这样就重用了已经有的软件中的一部分设置大部分,大大生了编程工作量,这就是常说的软件重用, 不仅可以重用自己的类,也可以继承别人的,比如标准库,来定制新的数据类型,这样就是大大缩短了软件开发周期,对大型软件开发来说 ,意义重大.
派生
当然子类也可以添加自己新的属性或者在自己这里重新定义这些属性(不会影响到父类),需要注意的是,一旦重新定义了自己的属性且与
父类重名,那么调用新增的属性时,就以自己为准了。
class Animal: ''' 人和狗都是动物,所以创造一个Animal基类 ''' def __init__(self, name, aggressivity, life_value): self.name = name # 人和狗都有自己的昵称; self.aggressivity = aggressivity # 人和狗都有自己的攻击力; self.life_value = life_value # 人和狗都有自己的生命值; def eat(self): print('%s is eating'%self.name) class Dog(Animal): ''' 狗类,继承Animal类 ''' def bite(self, people): ''' 派生:狗有咬人的技能 :param people: ''' people.life_value -= self.aggressivity class Person(Animal): ''' 人类,继承Animal ''' def attack(self, dog): ''' 派生:人有攻击的技能 :param dog: ''' dog.life_value -= self.aggressivity egg = Person('egon',10,1000) ha2 = Dog('二愣子',50,1000) print(ha2.life_value) print(egg.attack(ha2)) print(ha2.life_value)
super()实现子类执行父类的方法
class A: def hahaha(self): print('A') class B(A): def hahaha(self): super().hahaha() #super(B,self).hahaha() #A.hahaha(self) print('B') a = A() b = B() b.hahaha() super(B,b).hahaha()
派生演示
class Animal: ''' 人和狗都是动物,所以创造一个Animal基类 ''' def __init__(self, name, aggressivity, life_value): self.name = name # 人和狗都有自己的昵称; self.aggressivity = aggressivity # 人和狗都有自己的攻击力; self.life_value = life_value # 人和狗都有自己的生命值; def eat(self): print('%s is eating'%self.name) class Dog(Animal): ''' 狗类,继承Animal类 ''' def __init__(self,name,breed,aggressivity,life_value): super().__init__(name, aggressivity, life_value) #执行父类Animal的init方法 self.breed = breed #派生出了新的属性 def bite(self, people): ''' 派生出了新的技能:狗有咬人的技能 :param people: ''' people.life_value -= self.aggressivity def eat(self): # Animal.eat(self) #super().eat() print('from Dog') class Person(Animal): ''' 人类,继承Animal ''' def __init__(self,name,aggressivity, life_value,money): #Animal.__init__(self, name, aggressivity, life_value) #super(Person, self).__init__(name, aggressivity, life_value) super().__init__(name,aggressivity, life_value) #执行父类的init方法 self.money = money #派生出了新的属性 def attack(self, dog): ''' 派生出了新的技能:人有攻击的技能 :param dog: ''' dog.life_value -= self.aggressivity def eat(self): #super().eat() Animal.eat(self) print('from Person') egg = Person('egon',10,1000,600) ha2 = Dog('二愣子','哈士奇',10,1000) print(egg.name) print(ha2.name) egg.eat()
接口类
继承有两种用途:
一:继承基类的方法,并且做出自己的改变或者扩展(代码重用)
二:声明某个子类兼容于某基类,定义一个接口类Interface,接口类中定义了一些接口名(就是函数名)且并未实现接口的功能,子类继承
接口类,并且实现接口中的功能
from abc import ABCMeta, abstractmethod # 借用abc模块来实现接口,没有这个方法会报错 class Payment(metaclass=ABCMeta): @abstractmethod def pay(self, money): pass class Wechatpay(Payment): def fuqian(self, money): print('微信支付了%s元' % money) p = Wechatpay() # 不调就报错了 # TypeError: Can't instantiate abstract class Wechatpay with abstract methods pay
接口继承实质上是要求“做出一个良好的抽象,这个抽象规定了一个兼容接口,使得外部调用者无需关心具体细节,可一视同仁的处理实现 了特定接口的所有对象”——这在程序设计上,叫做归一化。
依赖倒置原则:高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该应该依赖细节;细节应该依赖抽象。换言之,要针对接口编程,而不是针对实现编程
为何要用接口??
接口提取了一群类共同的函数,可以把接口当做一个函数的集合。然后让子类去实现接口中的函数。这么做的意义在于归一化,什么叫归一化,就是只要是基于同一个接口实现的类,那么所有的这些类产生的对象在使用时,从用法上来说都 一样。
归一化,让使用者无需关心对象的类是什么,只需要的知道这些对象都具备某些功能就可以了,这极大地降低了使用者的使用难度。
比如:我们定义一个动物接口,接口里定义了有跑、吃、呼吸等接口函数,这样老鼠的类去实现了该接口,松鼠的类也去实现了该接口,由 二者分别产生一只老鼠和一只松鼠送到你面前,即便是你分别不到底哪只是什么鼠你肯定知道他俩都会跑,都会吃,都能呼吸。
再比如:我们有一个汽车接口,里面定义了汽车所有的功能,然后由本田汽车的类,奥迪汽车的类,大众汽车的类,他们都实现了汽车接口 ,这样就好办了,大家只需要学会了怎么开汽车,那么无论是本田,还是奥迪,还是大众我们都会开了,开的时候根本无需关心我开的是哪 一类车,操作手法(函数调用)都一样。
抽象类
抽象类是一个特殊的类,它的特殊之处在于只能被继承,不能被实例化
为什么要有抽象类?如果说类是从一堆对象中抽取相同的内容而来的,那么抽象类就是从一堆类中抽取相同的内容而来的,内容包括数据属性和函数属性。
比如我们有香蕉的类,有苹果的类,有桃子的类,从这些类抽取相同的内容就是水果这个抽象的类,你吃水果时,要么是吃一个具体的香蕉 ,要么是吃一个具体的桃子。。。。。。你永远无法吃到一个叫做水果的东西。
# 一切皆文件 import abc # 利用abc模块实现抽象类 class All_file(metaclass=abc.ABCMeta): all_type = 'file' @abc.abstractmethod # 定义抽象方法,无需实现功能 def read(self): '子类必须定义读功能' pass @abc.abstractmethod # 定义抽象方法,无需实现功能 def write(self): '子类必须定义写功能' pass class Txt(All_file): # 子类继承抽象类,但是必须定义read和write方法 def read(self): print('文本数据的读取方法') def write(self): print('文本数据的读取方法') class Sata(All_file): # 子类继承抽象类,但是必须定义read和write方法 def read(self): print('硬盘数据的读取方法') def write(self): print('硬盘数据的读取方法') class Process(All_file): # 子类继承抽象类,但是必须定义read和write方法 def read(self): print('进程数据的读取方法') def write(self): print('进程数据的读取方法') wenbenwenjian = Txt() yingpanwenjian = Sata() jinchengwenjian = Process() # 这样大家都是被归一化了,也就是一切皆文件的思想 wenbenwenjian.read() yingpanwenjian.write() jinchengwenjian.read() print(wenbenwenjian.all_type) print(yingpanwenjian.all_type) print(jinchengwenjian.all_type)
抽象类是一个介于类和接口直接的一个概念,同时具备类和接口的部分特性,可以用来实现归一化设计,在python中,并没有接口类这种东西,即便不通过专门的模块定义接口,我们也应该有一些基本的概念。
在继承抽象类的过程中,我们应该尽量避免多继承;
而在继承接口的时候,我们反而鼓励你来多继承接口
接口隔离原则:使用多个专门的接口,而不使用单一的总接口。即客户端不应该依赖那些不需要的接口。
继承顺序
class A(object): def test(self): print('from A') class B(A): def test(self): print('from B') class C(A): def test(self): print('from C') class D(B): def test(self): print('from D') class E(C): def test(self): print('from E') class F(D, E): # def test(self): # print('from F') pass f1 = F() f1.test() print(F.__mro__) # 只有新式才有这个属性可以查看线性列表,经典类没有这个属性 # 新式类继承顺序:F->D->B->E->C->A(广度优先) # 经典类继承顺序:F->D->B->A->E->C(深度优先)
继承原理
对于你定义的每一个类,python会计算出一个方法解析顺序(MRO)列表,这个MRO列表就是一个简单的所有基类的线性顺序列表,例如
[<class '__main__.F'>, <class '__main__.D'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>
为了实现继承,python会在MRO列表上从左到右开始查找基类,直到找到第一个匹配这个属性的类为止。而这个MRO列表的构造是通过一个C3线性化算法来实现的。
继承总结
继承的作用
减少代码的重用
提高代码可读性
规范编程模式
几个名词
抽象:抽象即抽取类似或者说比较像的部分。是一个从具题到抽象的过程。
继承:子类继承了父类的方法和属性
派生:子类在父类方法和属性的基础上产生了新的方法和属性
1.多继承问题
在继承抽象类的过程中,我们应该尽量避免多继承;
而在继承接口的时候,我们反而鼓励你来多继承接口
2.方法的实现
在抽象类中,我们可以对一些抽象方法做出基础实现;
而在接口类中,任何方法都只是一种规范,具体的功能需要子类实现
钻石继承
新式类:广度优先
经典类:深度优先
多态:多态指的是一类事物有多种形态(重写父类方法)
鸭子类型
Python崇尚鸭子类型,即‘如果看起来像、叫声像而且走起路来像鸭子,那么它就是鸭子’,python程序员通常根据这种行为来编写程序。例如,如果想编写现有对象的自定义版本,可以继承该对象
也可以创建一个外观和行为像,但与它无任何关系的全新对象,后者通常用于保存程序组件的松耦合度。也就是说,你可以继承他就是鸭子,重写就不是鸭子
封装(隐藏对象的属性和实现细节,仅对外提供公共访问方式。)
【好处】
1. 将变化隔离;
2. 便于使用;
3. 提高复用性;
4. 提高安全性;
【封装原则】
1. 将不需要对外提供的内容都隐藏起来;
2. 把属性都隐藏,提供公共方法对其访问。
私有变量和私有方法(在python中用双下划线开头的方式将属性隐藏起来(设置成私有的))
私有变量
# 其实这仅仅这是一种变形操作 # 类中所有双下划线开头的名称如__x都会自动变形成:_类名__x的形式: class A: __N = 0 # 类的数据属性就应该是共享的,但是语法上是可以把类的数据属性设置成私有的如__N,会变形为_A__N def __init__(self): self.__X = 10 # 变形为self._A__X def __foo(self): # 变形为_A__foo print('from A') def bar(self): self.__foo() # 只有在类内部才可以通过__foo的形式访问到. # A._A__N是可以访问到的,即这种操作并不是严格意义上的限制外部访问,仅仅只是一种语法意义上的变形 a = A() a.bar() print(a.__dict__)
这种自动变形的特点:
1.类中定义的__x只能在内部使用,如self.__x,引用的就是变形的结果。
2.这种变形其实正是针对外部的变形,在外部是无法通过__x这个名字访问到的。
3.在子类定义的__x不会覆盖在父类定义的__x,因为子类中变形成了:_子类名__x,而父类中变形成了:_父类名__x,即双下滑线开头的属性
在继承给子类时,子类是无法覆盖的。
私有方法
class A: def __fa(self): # 在定义时就变形为_A_ print('from A') def test(self): self.__fa() # 只会与自己所在的类为准,即调用_A__fa a = A() a.test()
封装在于明确区分内外,使得类实现者可以修改封装内的东西而不影响外部调用者的代码;而外部使用用者只知道一个接口(函数),只要接 口(函数)名、参数不变,使用者的代码永远无需改变。这就提供一个良好的合作基础——或者说,只要接口这个基础约定不变,则代码改变不足为虑。
#类的设计者 class Room: def __init__(self,name,owner,width,length,high): self.name=name self.owner=owner self.__width=width self.__length=length self.__high=high def tell_area(self): #对外提供的接口,隐藏了内部的实现细节,此时我们想求的是面积 return self.__width * self.__length #使用者 >>> r1=Room('卧室','egon',20,20,20) >>> r1.tell_area() #使用者调用接口tell_area #类的设计者,轻松的扩展了功能,而类的使用者完全不需要改变自己的代码 class Room: def __init__(self,name,owner,width,length,high): self.name=name self.owner=owner self.__width=width self.__length=length self.__high=high def tell_area(self): #对外提供的接口,隐藏内部实现,此时我们想求的是体积,内部逻辑变了,只需求修该下列一行就可以很简答的 实现,而且外部调用感知不到,仍然使用该方法,但是功能已经变了 return self.__width * self.__length * self.__high #对于仍然在使用tell_area接口的人来说,根本无需改动自己的代码,就可以用上新功能 >>> r1.tell_area()
接口就是提供一个对外的函数入口
property属性?什么是特性property?
property是一种特殊的属性,访问它时会执行一段功能(函数)然后返回值
import math class Circle: def __init__(self,radius): #圆的半径radius self.radius=radius @property def area(self): return math.pi * self.radius**2 #计算面积 @property def perimeter(self): return 2*math.pi*self.radius #计算周长 c=Circle(10) print(c.radius) print(c.area) #可以向访问数据属性一样去访问area,会触发一个函数的执行,动态计算出一个值 print(c.perimeter) #同上 ''' 输出结果: 314.1592653589793 62.83185307179586 '''
为什么要用property?
将一个类的函数定义成特性以后,对象再去使用的时候obj.name,根本无法察觉自己的name是执行了一个函数然后计算出来的,这种特性的使用方式遵循了统一访问的原则
【public】
这种其实就是不封装,是对外公开的
【protected】
这种封装方式对外不公开,但对朋友(friend)或者子类(形象的说法是“儿子”,但我不知道为什么大家 不说“女儿”,就像“parent”本来是
“父母”的意思,但中文都是叫“父类”)公开
【private】
这种封装对谁都不公开
在C++里一般会将所有的所有的数据都设置为私有的,然后提供set和get方法(接口)去设置和获取
不允许在外部修改和删除
class Goods: 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 # 修改商品原价 print(obj.price) del obj.price # 删除商品原价
类方法classmethod
class Classmethod_Demo(): role = 'dog' @classmethod def func(cls): print(cls.role) Classmethod_Demo.func()
静态方法staticmethod
class Staticmethod_Demo(): role = 'dog' @staticmethod def func(): print("当普通方法用") Staticmethod_Demo.func()
真正的封装是,经过深入的思考,做出良好的抽象,给出“完整且最小”的接口,并使得内部细节可以对外透明
(注意:对外透明的意思是,外部调用者可以顺利的得到自己想要的任何功能,完全意识不到内部细节的存在)
自省/反射
自省也称作反射,这个性质展示了某对象是如何在运行期取得自身信息的。如果传一个对象给你,你可以查出它有什么能力,这是一项强大的特性。如果Python不支持某种形式的自省功能,dir和type内建函数,将很难正常工作。还有那些特殊属性,像__dict__,__name__及__doc__
isinstance和issubclass
class Foo(object): pass class Bar(Foo): pass obj = Foo() print(isinstance(obj, Foo)) # isinstance(obj,cls)检查是否obj是否是类 cls 的对象 print(issubclass(Bar, Foo)) # issubclass(sub, super)检查sub类是否是 super 类的派生类
什么是反射?主要是指程序可以访问、检测和修改它本身状态或行为的一种能力(自省)。
python面向对象中的反射:通过字符串的形式操作对象相关的属性。python中的一切事物都是对象(都可以使用反射)
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__)
反射的作用,导入其他模块,利用反射查找该模块是否存在某个方法
def test(): print('from the test') """ 程序目录: module_test.py index.py 当前文件: index.py """ import module_test as obj #obj.test() print(hasattr(obj,'test')) getattr(obj,'test')() __str__和__repr__ class B: def __str__(self): return 'str : class B' def __repr__(self): return 'repr : class B' b = B() # str函数或者print函数--->obj.__str__() # repr或者交互式解释器--->obj.__repr__() # 如果__str__没有被定义,那么就会使用__repr__来代替输出 # 注意:这俩方法的返回值必须是字符串,否则抛出异常 print('%s' % b) # str : class B print('%r' % b) # repr : class B __del__ 析构方法,当对象在内存中被释放时,自动触发执行。 class Foo: def __del__(self): print('执行我啦') f1 = Foo() del f1 print('------->') __new__:单例模式 class Singleton: def __new__(cls, *args, **kw): if not hasattr(cls, '_instance'): cls._instance = object.__new__(cls) return cls._instance one = Singleton() two = Singleton() two.a = 3 print(one.a) # 3 # one和two完全相同,可以用id(), ==, is检测 print(id(one)) # 38510664 print(id(two)) # 38510664 print(one == two) # True print(one is two) __call__ 对象后面加括号,触发执行。 注:构造方法的执行是由创建对象触发的,即:对象 = 类名() ;而对于 __call__ 方法的执行是由对象后加括号触发的,即:对象() 或者 类()() class Foo: def __init__(self): pass def __call__(self, *args, **kwargs): print('__call__') obj = Foo() # 执行 __init__ obj() # 执行 __call__ __len__ class A: def __init__(self): self.a = 1 self.b = 2 def __len__(self): return len(self.__dict__) a = A() print(len(a)) __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)) __eq__ class A: def __init__(self): self.a = 1 self.b = 2 def __eq__(self,obj): if self.a == obj.a and self.b == obj.b: return True a = A() b = A() print(a == b)
一道面试题
class Person: def __init__(self, name, age, sex): self.name = name self.age = age self.sex = sex def __hash__(self): return hash(self.name + self.sex) def __eq__(self, other): if self.name == other.name and self.sex == other.sex: return True p_lst = [] for i in range(84): p_lst.append(Person('egon', i, 'male')) print(p_lst) print(set(p_lst))