类与对象
大约 18 分钟
两种变量
# 定义类可以加`()`,也可以不加`()`,如果不加`()`,就是`新式类`
class User:
# 成员变量
name = 'lixingyun'
age = 19
# 定义实例变量
def __init__(self, gender, address):
self.gender = gender
self.address = address
print("访问实例变量 ==>", self.name)
# 只能通过以下两种方式访问成员变量
print("访问成员变量方式一 ==>", User.name)
print("访问成员变量方式二 ==>", self.__class__.name)
# 打印类的成员变量
user = User('male', 'beijing') # print("访问实例变量 ==>", self.name)
# 访问成员变量方式一 ==> lixingyun
# 访问成员变量方式二 ==> lixingyun
print(User.name, User.age) # lixingyun 19
print(user.name, user.age) # lixingyun 19
# 类修改成员变量的值,值会改变
User.name = 'wanglin'
User.age = 20
print(User.name, User.age) # wanglin 20
print(user.name, user.age) # wanglin 20
# 实例修改成员变量的值,成员变量的值不会改变,但实例变量的值会变
# 赋值时会给实例user创建两个新的变量 name 和 age
user.name = '唐三'
user.age = 30
print(user.name, user.age) # 唐三 30
print(User.name, User.age) # wanglin 20
# 打印类的实例变量
print(user.gender, user.address) # male beijing
# 修改实例变量的值
user.gender = 'female'
user.address = 'shanghai'
# 再次打印类的实例变量
print(user.gender, user.address) # female shanghai
# 实例对象可以访问类的成员变量,但类不能访问实例对象的实例变量,否则会报错
# print(User.gender, User.address)
# 总结:
# 1. 成员变量属于类,实例变量属于实例
# 2. 类修改成员变量后,当实例访问它时,访问的是改变后的值
# 3. 实例修改成员变量时,python会给它创建一个新的同名的本地实例变量,并赋值给它
# 查看类的成员变量和实例的实例变量之间的不同
print("实例user.__dict__ ==>", user.__dict__) # 实例user.__dict__ ==> {'gender': 'female', 'address': 'shanghai', 'name': '唐三', 'age': 30}
print("类User.__dict__ ==>", User.__dict__) # 类User.__dict__ ==> {'__module__': '__main__', 'name': 'wanglin', 'age': 20, '__init__': <function User.__init__ at 0x7fdeb801ec10>, '__dict__': <attribute '__dict__' of 'User' objects>, '__weakref__': <attribute '__weakref__' of 'User' objects>, '__doc__': None}

四种方法
class Person:
gender = 'male'
age = 25
def __init__(self, name, age):
# self是一个Python会固定传入的关键字,相当于this,不需要人为传入
# self的意义是Python之禅中的:显胜于隐
self.name = name
self.age = 1
# 除了None,构造函数什么都不能返回
# return None
def __str__(self):
return 'Person: name: ' + self.name + ', age: ' + str(self.age)
# 如果是实例方法,那么方法的第一个参数需要传入固定的关键字self,self相当于this
# self就是调用方法的实例对象,self只与对象有关
def show1(self):
print('实例方法show1: name:', self.name, ', age:', self.age)
def show2(self, name, age):
print('实例方法show2: name:', name, ', age:', age)
# 定义类方法,默认传入的参数名称为cls
# 可以将cls替换成self,仍然是类方法,并没有因为使用self而变成实例方法
# 有 @classmethod 注解的方法,类和对象都可以调用
@classmethod
def show3(cls):
# 虽然是类方法,但调用的还是实例变量 self.age = 1,这里即使换成调用 Person.age ,打印出来结果也是 age: 1
print("类方法show3-1: ", cls("customer", Person.age))
print("类方法show3-2: ", cls("customer", cls.age))
@classmethod
def show4(self, gender):
# 通过cls调用成员变量
print('类方法show4: gender:', gender)
# 另一种定义类方法的方式,这种类方法只能类调用,对象调用时会报错
def show5(name, age):
print('类方法show5-1: name:', name, ', age:', age)
# 一旦去掉@classmethod修饰,Person.age 调用就是成员变量
print('类方法show5-2: name:', name, ', age:', Person.age)
# 定义一个静态方法,类和对象都可以调用,且不需要self,也不需要cls
@staticmethod
def show6(customer):
# 因为静态方法是没有 self 的,所以只能通过类名调用构造方法
# 如果类名发生变化,那么静态方法中的类名也要修改
# 而类方法可以通过 cls 或 self 自动改变类型,这是静态方法不如类方法的地方
# 所以,当不需要传入cls参数的时候,就可以用静态方法来代替类方法
print(Person(customer, 25))
person = Person('lixingyun', 19)
print(type(person)) # <class '__main__.Person'>
print(person)
# 可以显式地调用构造函数,这个构造函数覆盖掉了之前构造函数给name和age的赋值
res = person.__init__('xiaoyan', 21) # Person: name: lixingyun, age: 1
print(type(res)) # <class 'NoneType'>
print(res) # None
person.show1() # 实例方法show1: name: xiaoyan , age: 1
person.show2("wanglin", 22) # 实例方法show2: name: wanglin , age: 22
# 调用类方法
Person.show3() # 类方法show3-1: Person: name: customer, age: 1
# 类方法show3-2: Person: name: customer, age: 1
Person.show4("女") # 类方法show4: gender: 女
# 对象实例可以调用有@classmethod注解和cls参数的类方法,下面的调用可以正常执行
person.show3() # 类方法show3: Person: name: customer, age: 1
# 类方法show3-2: Person: name: customer, age: 1
person.show4("女") # 类方法show4: gender: 女
# 但不能调用没有@classmethod注解和cls参数的类方法,下面的调用会报错
# person1.show5('luofeng', 20) # TypeError: show5() takes 2 positional arguments but 3 were given
# 但类可以调用没有@classmethod注解和cls参数的类方法
Person.show5('luofeng', 20) # 类方法show5-1: name: luofeng , age: 20
# 类方法show5-2: name: luofeng , age: 25
# 同样,类不能调用实例方法
# Person.show1() # TypeError: show1() missing 1 required positional argument: 'self'
# 在静态方法的调用方式和结果上,类和对象是一样的
# 静态方法和类方法的最大区别就是是否传入cls参数,它类似于self,但却是对类的引用而不是对象实例
Person.show6("lixingyun") # Person: name: lixingyun, age: 1
person.show6("wanglin") # Person: name: wanglin, age: 1
print("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
from abc import ABC, abstractmethod
class Student1(ABC):
# 定义一个抽象方法
@abstractmethod
def show(self):
pass
class Student2(Student1):
# 实现抽象方法
def show(self):
print("Student2")
# 继承自ABC的类无法被实例化,这样使用将报错
# student1 = Student1()
# 只有这样才能正常使用抽象类和抽象方法
student2 = Student2()
print(student2.show()) # Student2
# 但只有抽象方法的抽象类和正常的类没什么区别
class Student3:
# 定义一个抽象方法
@abstractmethod
def show(self):
pass
student3 = Student3()
print(student3.show()) # None
# 子类也不必实现抽象方法
class Student4(Student3):
def noShow(self):
print("Student3")
# 没有实现抽象方法的子类一样可以正常使用
student4 = Student4()
# 调用未实现的抽象方法
student4.show()
student4.noShow() # Student3
私有属性
class Person:
# 如果想让公开的成员变量或成员方法变成私有的,可以给它们加上 `__` (两个下划线)前缀
__name = 'lixingyun'
__age = 19
def __init__(self, name, age):
self.__name = name
self.__age = age
def __show1(self):
print('hello from show1() ==>', self.__name, self.__age)
def show2(self):
# 当show2()变成私有方法以后,就只能在内部调用它,也可以这样调用:Person.__show2(self)
self.__show1()
print('hello from show2() ==>', self.__name)
# 在公共方法中调用私有成员变量
def show3(self):
print('show3 ==>', self.__name)
# 在类方法中调用私有成员变量
@classmethod
def show4(cls, name):
cls.__name = name
print('show4 ==>', cls.__name)
# 在静态方法中调用私有成员变量
@staticmethod
def show5(name):
Person.__name = name
print('show5 ==>', Person.__name)
# 调用构造方法
person1 = Person("xiaoyan", 21)
# 通过公共方法来调用私有方法
person1.show2() # hello from show1() ==> xiaoyan 21
# hello from show2() ==> xiaoyan
# 仍然可以通过类名或实例名来调用私有成员变量,所以python没有什么所谓的绝对安全
print(person1._Person__name) # xiaoyan
# 此时就不能通过类名或实例名直接访问成员变量了
# print('hello', Person.name)
# 也不能访问私有方法show1()了
# person1.show1()
# 只能通过公共方法来访问私有成员变量
person1.show3() # show3 ==> xiaoyan
person1.show4("wanglin") # show4 ==> wanglin
person1.show5("qinyu") # show5 ==> qinyu
Person.show5("lindong") # show5 ==> lindong
Person.show5("luofeng") # show5 ==> luofeng
print("赋值之前的person1的成员变量:", person1.__dict__) # 赋值之前的person1的成员变量: {'_Person__name': 'xiaoyan', '_Person__age': 21}
person1.__name = "shihao1"
print("person1.__name ==>", person1.__name) # person1.__name ==> shihao1
print("赋值之后的person1的成员变量:", person1.__dict__) # 赋值之后的person1的成员变量: {'_Person__name': 'xiaoyan', '_Person__age': 21, '__name': 'shihao1'}
# 上面的 person1.__name 之所以能够成功赋值和访问,是因为赋值语句给 person1 重新生成了一个 __name 变量
# 这一点可以通过打印 person1.__dict__ 的值看得很清楚,它只不过是把名字换了而已
# 所以如果没有这种重新生成的变量,直接访问私有成员变量 __name 的话,仍然会报错
person2 = Person("tangsan", 22)
# 此时 person2 访问的是类的私有成员变量 __name,而不是给 person1 重新生成的 __name 变量,所以下面的打印会报错
# print("person2.__name ==>", person2.__name)
# 但是如果通过调用 Person.__name = "shihao2" ,这就相当于给类新生成了一个公共的 __name 变量
# 那么当打印 person2.__name 的时候,就会打印类的成员变量 __name 的值
Person.__name = "shihao2"
print("Person.__name ==>", Person.__name) # Person.__name ==> shihao2
print("person2.__name ==>", person2.__name) # person2.__name ==> shihao2
# 因此可以这样来访问私有成员变量
print("真正的私有变量person1.__name ==>", person1._Person__name) # 真正的私有变量person1.__name ==> xiaoyan
# 但不建议这样做
person1._Person__name = "在外部改变私有变量的值"
print("改变后的私有变量person1.__name ==>", person1._Person__name) # 改变后的私有变量person1.__name ==> 在外部改变私有变量的值
print("真正的私有变量person1.__name ==>", person1._Person__name) # 真正的私有变量person1.__name ==> 在外部改变私有变量的值
多重继承
# 父亲
class Father():
name = 'lixingyun'
age = 38
share1 = "call me Father"
share2 = "father"
# 父类的构造方法
def __init__(self, name, age):
self.name = name
self.age = age
# 父类和母类有一样的方法名
def show(self):
print('father ==>', self.name, self.age)
# 母亲
class Mother():
name = 'jiruxue'
age = 36
gender = 'female'
share1 = "call me Mother"
share3 = "mother"
# 母类的构造方法
def __init__(self, name, age, gender):
self.name = name
self.age = age
self.gender = gender
# 父类和母类有一样的方法名
def show(self):
print('mother ==>', self.name, self.age, self.gender)
# 儿子
class Son(Mother, Father):
# 1. 当子类实现多继承时(父母类),子类的构造函数除了可以增加自己的成员变量外(这里是score),必须满足继承列表中第一个类(这里是母类Mother)的构造方法的要求
# 例如 super().__init__(name, age, gender),这里的super就是Mother,而不是Father
# 2. 如果父类A和母类B的成员变量名字相同,那么子类只能继承第一个类(这里是母类Mother)的成员变量,而不会继承第二个类(父类Father)的成员变量,例如 share1
# 如果父类A和母类B的成员变量名字不同,那么子类就都可以继承到它们的值,例如 share2 和 share3
# 3. 这个规则对成员方法也适用
def __init__(self, name, age, gender, score):
# 调用父类或者母类的构造方法(调用形式视继承列表中第一个类的情况而定)
super().__init__(name, age, gender)
# 或者这样调用
# super(Son, self).__init__(name, age, gender)
# Father.__init__(self, name, age)
# Mother.__init__(self, name, age, gender)
self.score = score
def goodson(self):
print('son ==>', self.name, self.age, self.gender, self.score)
def show(self):
# 这里调用的是母类的方法
super().show()
print('this is son\'s show ==>', self.name, self.age, self.gender, self.score)
# 这是子类的构造方法,必须满足母类(Mother)构造方法的要求
son1 = Son('tangsan', 20, 'unknown', 98)
# 这里调用的是子类的实例方法
son1.goodson()
# 如果成员变量名字相同(例如都叫share1),那么打印出来的是母类Mother中(share1)的值,而不是父类Father中(share1)的值
print(son1.share1)
# 如果成员变量名字不同,那么打印出来的是父类Father和母类Mother中各自成员变量的值
print(son1.share2)
print(son1.share3)
son1.show()
# 如果子类没有定义构造函数,则必须按照继承列表中第一个类的形式调用构造函数
# 如果子类定义了构造函数,再用父类或母类构造函数的形式调用就会报错
# son2 = Son('tangsan', 19, 'Male')
# son2.goodson()
print("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
class A():
name = 'A'
def __init__(self):
self.name = "a"
print("这是A的构造方法")
super().__init__()
def show(self):
print("这是A的show方法")
class B():
name = 'B'
def __init__(self):
self.name = "b"
print("这是B的构造方法")
super().__init__()
class C(A):
name = 'C'
def __init__(self):
self.name = "c"
print("这是C的构造方法")
super().__init__()
class D(B):
name = 'D'
def __init__(self):
self.name = "d"
print("这是D的构造方法")
super().__init__()
def show(self):
print("这是D的show方法")
# MRO机制:Method Research Order
class E1(C, D):
name = 'E1'
def __init__(self):
self.name = "e1"
print("这是E1的构造方法")
super().__init__()
class E2(D, C):
name = 'E2'
def __init__(self):
self.name = "e2"
print("这是E2的构造方法")
super().__init__()
# C3算法:按照继承顺序查找,从下往上,从左往右(左右指的是继承列表中的位置),且深度优先
# 打印出C3算法的调用路径
#
print(E1.mro()) # [<class '__main__.E1'>, <class '__main__.C'>, <class '__main__.A'>, <class '__main__.D'>, <class '__main__.B'>, <class 'object'>]
print(E2.mro()) # [<class '__main__.E2'>, <class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
# 所谓深度优先,其顺序是:E -> C -> A -> D -> B,但先找C还是先找D,取决于 C 和 D 在继承列表中的位置
# super() 方法和普通方法不同,它会调用每一个找到的父类的 super() 方法,而不像普通方法一样,只调用匹配的第一个
# 但是在多重继承中,super() 的真正含义并不是调用父类的 __init__() 方法,而是会调用MRO中下一个父类的 __init__() 方法
e1 = E1() # 这是E1的构造方法
# 这是C的构造方法
# 这是A的构造方法
# 这是D的构造方法
# 这是B的构造方法
# 因为当执行完 B 的 __init__() 方法后,self.name 的值已经变成了 b
print(e1.name) # b
# 对于普通方法,当找到第一个匹配的show()方法后直接返回,不会再继续往下查找
e1.show() # 这是A的show方法
e2 = E2() # 这是E2的构造方法
# 这是D的构造方法
# 这是B的构造方法
# 这是C的构造方法
# 这是A的构造方法
# 执行结果的道理和上面的 E1 类是一样的
print(e2.name) # a
# 执行结果的道理和上面的 E1 类是一样的
e2.show() # 这是D的show方法
print("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
class F():
name = 'F'
def __init__(self):
self.name = "f"
print("这是F的构造方法")
super().__init__()
def show(self):
print("这是F的show方法")
class G(F):
name = 'G'
def __init__(self):
self.name = "g"
print("这是G的构造方法")
super().__init__()
class H(F):
name = 'H'
def __init__(self):
self.name = "h"
print("这是H的构造方法")
super().__init__()
def show(self):
print("这是H的show方法")
class I1(G, H):
def __init__(self):
self.name = "i1"
print("这是I1的构造方法")
super().__init__()
class I2(H, G):
def __init__(self):
self.name = "i2"
print("这是I2的构造方法")
super().__init__()
# C3算法:按照继承顺序查找,从下往上,从左往右(左右指的是继承列表中的位置),且广度优先
print(I1.mro()) # [<class '__main__.I1'>, <class '__main__.G'>, <class '__main__.H'>, <class '__main__.F'>, <class 'object'>]
print(I2.mro()) # [<class '__main__.I2'>, <class '__main__.H'>, <class '__main__.G'>, <class '__main__.F'>, <class 'object'>]
i1 = I1() # 这是I1的构造方法
# 这是G的构造方法
# 这是H的构造方法
# 这是F的构造方法
# super() 方法和普通方法不同,它会调用每一个找到的父类的 super() 方法,而不像普通方法一样,只调用匹配的第一个,所以结果是 f
# 但是在多重继承中,super() 的真正含义并不是调用父类的 __init__() 方法,而是会调用MRO中下一个父类的 __init__() 方法
print(i1.name) # f
# 不管是深度优先还是广度优先,对于普通方法而言,当找到第一个匹配的show()方法后直接返回,不会再继续往下查找
i1.show() # 这是H的show方法
i2 = I2() # 这是I2的构造方法
# 这是H的构造方法
# 这是G的构造方法
# 这是F的构造方法
# 道理同上
print(i2.name) # f
# 道理同上
i2.show() # 这是H的show方法
# 多重继承的优秀设计:Django中的mixin混合模式
# 1. 遵循单一职责原则(Single Responsibility Principle,SRP),将职责分离,将不同的功能封装到不同的类中,避免类职责过重
# 2. 不和基类关联,可以和任意基类组合,而且基类不和mixin关联就能初始化成功,符合合成复用原则(Composite Reuse Principle,CRP)
# 3. 在mixin中不使用super()方法,避免MRO机制

抽象基类
# 需要强制某个子类必须实现某些方法,例如数据库Dao的连接方法
import abc
class BaseDao(metaclass=abc.ABCMeta):
# 通过指定某个方法为抽象方法的方式,让子类必须来实现它
@abc.abstractmethod
def connection(self, url, port, user, password):
print('connect to database')
# 如果不使用抽象基类的 metaclass=abc.ABCMeta ,那么就必须抛出异常
# raise NotImplementedError()
@abc.abstractmethod
def query(self, sql):
print('query data')
class MySQLDao(BaseDao):
# 必须实现抽象方法connection,否则还是会报错
# def connection(self, url, port, user, password):
# print('connect to mysql')
# 实现抽象方法query
def query(self, sql):
print('query data from mysql')
mysqlDao = MySQLDao()
# 如果不实现connection()方法,使用时会报错
# 但如果对抽象基类的理解不足,就不要轻易使用它
mysqlDao.connection('localhost', 3306, 'root', '123456')
对象自省
# 自省是通过一定的机制查询到对象的内部结构
class User:
# name是User的成员变量
name = 'lixingyun'
def __init__(self):
print("User init")
class Student(User):
def __init__(self, score):
self.score = score
student = Student(99)
# 通过__dict__查询属性
print(student.__dict__) # {'score': 99}
# 可以通过__dict__添加属性
student.__dict__["second_name"] = "wanglin"
print(student.second_name) # wanglin
# 但这样对象实例的 __dict__ 添加的属性,类是不能访问的,否则报错
# print(Student.second_name)
# 类不支持给 __dict__ 添加属性
# Student.__dict__["second_name2"] = "xiaoyan"
print(User.__dict__) # {'__module__': '__main__', 'name': 'lixingyun', '__dict__': <attribute '__dict__' of 'User' objects>, '__weakref__': <attribute '__weakref__' of 'User' objects>, '__doc__': None}
# 根据MRO向上查找,找到 User.__dict__ 中的 name 属性
print(student.name) # lixingyun
# 列出对象中所有的属性
print(dir(student)) # ['__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__', 'name', 'score', 'second_name']
a = [1, 2, 3]
# 这样打印会报错
# print(a.__dict__)
print(dir(a)) # ['__add__', '__class__', '__class_getitem__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']
class Person(User):
def __init__(self):
# 当为多重继承的时候,super()会根据MRO向上查找,并调用链条中下一个类的super()
super().__init__()
print("Person init")
person = Person() # User init
# Person init
经典传值错误
# 关于变量传值的经典误区
def add(a, b):
a += b
return a
# 第一种情况
a = 1
b = 2
print("add(a, b) ==>", add(a, b)) # add(a, b) ==> 3
# a 没有变化
print(a, b) # 1 2
# 第二种情况
a = [1, 2]
b = [3, 4]
print("add(a, b) ==>", add(a, b)) # add(a, b) ==> [1, 2, 3, 4]
# a 变成了 [1,2,3,4]
print(a, b) # [1, 2, 3, 4] [3, 4]
# 第三种情况
a = (1, 2)
b = (3, 4)
print("add(a, b) ==>", add(a, b)) # add(a, b) ==> (1, 2, 3, 4)
print(a, b) # (1, 2) (3, 4)
class Company:
def __init__(self, name, staffs=[]):
self.name = name
self.staffs = staffs
def add(self, staff_name):
self.staffs.append(staff_name)
def remove(self, staff_name):
self.staffs.remove(staff_name)
company1 = Company('itechthink1', ['lixingyun'])
company1.add('wanglin')
print(company1.staffs) # ['lixingyun', 'wanglin']
company1.remove('lixingyun')
print(company1.staffs) # ['wanglin']
# 因为 company1 传递了 staffs 参数,所以 company1 就不会使用默认的空列表[]
print(Company.__init__.__defaults__) # ([],)
company2 = Company('itechthink2')
company2.add('shihao')
print(company2.staffs) # ['shihao']
company3 = Company('itechthink3')
company3.add('luofeng')
print(company3.staffs) # ['shihao', 'luofeng']
# company2 和 company3 是两个不同的对象,但 staffs 却指向同一个列表
# 原因1:传递进来了一个可变的list
# 原因2:因为 company2 和 company3 都没有传递 staffs 参数,所以共同使用 Company 类中定义的 staffs
print(company2.staffs is company3.staffs) # True
# 这个默认的 staffs 可以直接打印出来
print(Company.__init__.__defaults__) # (['shihao', 'luofeng'],)
True和False
class TestClazz1():
pass
test1 = TestClazz1()
# test1不等于None
print("test1 ==>", test1) # test1 ==> <__main__.TestClazz1 object at 0x7fa5a80b5be0>
print("test1 == None ==>", test1 == None) # test1 == None ==> False
print(type(test1)) # <class '__main__.TestClazz1'>
print("test1 is True ==>", test1 is True) # test1 is True ==> False
print("test1 == True ==>", test1 == True) # test1 == True ==> False
# 这里是True
if test1:
print('print: test1 is True') # print: test1 is True
class TestClazz2():
# 如果返回0,那么实例会被判定为False
def __len__(self):
return 0
# 如果返回False,那么实例会被判定为False
def __bool__(self):
return False
test2 = TestClazz2()
# test2也不等于None
print("test2 ==>", test2) # test2 ==> <__main__.TestClazz2 object at 0x7fa5a80b5b20>
print("test2 == None ==>", test2 == None) # test2 == None ==> False
print(type(test2)) # <class '__main__.TestClazz2'>
print("test2 is True ==>", test2 is True) # test2 is True ==> False
print("test2 == True ==>", test2 == True) # test2 == True ==> False
# 但这里并不是True,而是False
# 因为内置函数__len__(self)和__bool__(self)的返回值为0和False,导致实例test2被判断为False
if test2:
print('print: test2 is True') #
class TestClazz3():
# 返回True
def __len__(self):
print("__len__() is called")
return True
# 返回False
def __bool__(self):
print("__bool__() is called")
# 如果这里使用 return 0 ,那么调用时将会报错
return False
test3 = TestClazz3()
# 如果__bool__()和__len__()同时存在,那么会以__bool__()为准,否则以__len__()为准
# 判断bool时,仅调用了__bool__()方法
print("bool(test3) ==>", bool(test3)) # __bool__() is called
# bool(test3) ==> False
# 因为__len__()返回True,所以len(test3)的结果是1,且获取长度时,也仅调用了__len__()方法
# 如果TestClazz3中没有__len__(),那么len(test3)将会报错
print("len(test3) ==>", len(test3)) # __len__() is called
# len(test3) ==> 1
type、class和object
# type -> int -> 1
print(type(1)) # <class 'int'> 数字 1 由 int 类生成
print(type(int)) # <class 'type'> int 由 type 类生成
# type -> str -> "abc"
print(type("abc")) # <class 'str'> 同上
print(type(str)) # <class 'type'> 同上
class Person:
pass
class Student(Person):
pass
person = Person()
student = Student()
names1 = ()
names2 = []
names3 = {}
names4 = set()
# object是顶层基类,所有的class都继承自object
# type本身也是一个class,它同时也是一个object
print(type(person)) # <class '__main__.Person'>
print(type(student)) # <class '__main__.Student'>
print(type(Person)) # <class 'type'>
print(type(Student)) # <class 'type'>
print(type(type)) # <class 'type'>
print(type(tuple)) # <class 'type'>
print(type(list)) # <class 'type'>
print(type(dict)) # <class 'type'>
print(type(set)) # <class 'type'>
print(type(tuple.__bases__)) # <class 'tuple'>
print(type(list.__bases__)) # <class 'tuple'>
print(type(dict.__bases__)) # <class 'tuple'>
print(type(set.__bases__)) # <class 'tuple'>
print(type(names1)) # <class 'list'>
print(type(names2)) # <class 'tuple'>
print(type(names3)) # <class 'dict'>
print(type(names4)) # <class 'set'>
print(int.__bases__) # (<class 'object'>,)
print(str.__bases__) # (<class 'object'>,)
print(Person.__bases__) # (<class 'object'>,)
print(Student.__bases__) # (<class '__main__.Person'>,)
print(type.__bases__) # (<class 'object'>,)
print(object.__bases__) # ()
print(type(object)) # <class 'type'>
type
、class
和object
这三者的关系可以用一张图来表示。

type
、class
和object
三者的关系感觉type
更加类似于接口
一样的存在。
type和isinstance
class Organization:
pass
class Company(Organization):
pass
org = Organization()
com = Company()
# isinstance 判断一个对象是否属于某个类,它会顺着继承链向上查找,找到则为True,否则为False
print(isinstance(com, Company))
print(isinstance(com, Organization)) # True
# `is` 不等同于 `==`
# is 用于判断两个对象是否相同
# == 用于判断两个值是否相等
print(type(com) is Company)
print(type(com) is Organization) # False
# 因此在判断对象是否相等时,尽量使用isinstance
感谢支持
更多内容,请移步《超级个体》。