Python Class 详解
Python Class 详解
1、基本概念
1.1 类 class
1.2 对象 instance / object
1.3 面向对象三要素
1 封装
组装:将数据和操作组装到一起
隐藏数据:对外只暴露一些接口,通过接口访问对象
2 继承
多复用,继承来的就不用自己写了
多继承少修改,OCP(open-closed principle),使用继承来改变,来体现个性
3 多态
面向对象编程最灵活的地方,动态绑定
1.4 哲学思想
2、类
2.1 类的定义
class ClassName:
语句块
2.2 类对象及类属性
2.3 类的实例化
Python
类实例化后,会自动调用 __init__
方法,这个方法的第一个形式参数必须留给 self
,其它参数随意2.4 __init__
方法
__init__(self)
方法self
return None
__init__
魔术方法来进行初始化-出厂配置,对生成的实例进行属性配置,__init__
定义的是实例的属性2.5 实例对象 instance
__init__
方法的第一参数 self 就是指代某一个实例自身instance.class_method()
的方式,实例对象就会绑定到方法上self
参数,就是 实例自身__init__
中所定义的变量,是保存在 实例 上的,并不是 类 上,所以,称为 实例变量2.6 示例 1
class MyClass:
"""My first class"""
print('in class')
xx = 'abc' # 类的属性
def __init__(self, x, y):
print('in init')
self.x = x
self.y = y
def foo(self):
print('in foo')
return "My class"
print(MyClass.xx, MyClass.foo, MyClass.__doc__)
myc1 = MyClass(1, 2)
myc1 = MyClass(1, 2)
print(myc1.foo())
print(MyClass.foo(myc1))
# 上例中 xx 和 foo 都是类的属性,__doc__ 也是类的特殊属性
# foo 是 方法method,本质上就是普通函数对象 function
# 第一个形式参数 self,指代当前实例本身
in class
abc <function MyClass.foo at 0x000002A221E76F70> My first class
in init
in init
in foo
My class
in foo
My class
2.7 示例 2
# self
class MyClass:
def __init__(self):
print(1, 'self in init = {}'.format(id(self)))
def showself(self):
print(2, 'self in showself() = {}'.format(id(self)))
c = MyClass()
print('After instance c')
print(3, 'c = {}'.format(id(c)))
print('=' * 30)
c.showself()
print(MyClass.showself)
print(c.showself)
# 类实例化后,得到一个实例对象,调用类方法时,采用 instance.class_method() 的方式,实例对象就会绑定到方法
# self 就是调用者,self 这个名字只是一个惯例,可以修改,但最好不要修改
# 查看打印结果,思考一下代码的执行顺序
1 self in init = 2895381183888
After instance c
3 c = 2895381183888
==============================
2 self in showself() = 2895381183888
<function MyClass.showself at 0x000002A221D851F0>
<bound method MyClass.showself of <__main__.MyClass object at 0x000002A2222ABD90>>
2.8 示例 3
# 实例变量和类变量
# 实例变量是每一个实例自己的变量,是自己独有的
# 类变量是类的变量,是类的所有实例共享的属性和方法
class Person:
age = 3
def __init__(self, name):
self.name = name
tom = Person('Tom')
jerry = Person('Jerry')
print(tom.name, jerry.name)
print(tom.age, jerry.age)
print(Person.age)
Person.age = 30
print(Person.age, tom.age, jerry.age)
Tom Jerry
3 3
3
30 30 30
3、对象属性
3.1属性介绍
1 特殊属性 __name__ __class__ __dict__ __qualname__
2 Python
中每一种对象都拥有不同的属性
3 函数、类都是对象,类的实例也是对象
4 类属性保存在类的__dict__
中,实例属性保存在实例的__dict__
中
5 属性是类的,也是这个类所有实例的,其它实例都可以访问到
6 属性是实例的,就是这个实例自己的,通过类访问不到
7 对象(实例或类)可以动态的给自己增加一个属性(赋值即定义一个新属性)
8 实例.__dict__[变量名]
和 实例.变量名
都可以访问到实例自己的属性(注意这两种访问是有本质区别的)
9 实例的同名变量会隐藏掉类变量,或者说是覆盖了这个类变量,但是注意类变量还在那里,并没有真正的被覆盖
10 类方法(也是类属性)在定义时,第一个参数必须时 self
,而 self
必须指向一个对象,也就是类实例化之后,由实例来调用这个方法
class Person:
pass
Person.__name__ # 'Person'
Person.__class__.__name__, type(Person).__name__ # ('type', 'type')
# 结果是字符串
type(Person), Person.__class__ # (type, type)
# 结果是类
3.2 实例属性的查找顺序
.点号
来访问属性,会先找自己的 __dict__
,如果没有,再通过属性 __class__
找到自己的类,再去类的 __dict__
中找__dict__[变量名]
来访问变量,就不会按照上面的查找顺序找变量了,这是指明使用字典的 key
查找,不是属性查找3.3 示例 1
class Person:
age = 3
def __init__(self, name):
self.name = name
print('----- class -----')
print(Person.__class__, type(Person))
print(sorted(Person.__dict__.items()))
print('=' * 66)
tom = Person('Tom')
print(tom.__class__, type(tom))
print(sorted(tom.__dict__.items()))
print('=' * 66)
print(tom.__class__.__name__)
print(sorted(tom.__class__.__dict__.items()))
print(sorted(type(tom).__dict__.items()))
----- class -----
<class 'type'> <class 'type'>
[('__dict__', <attribute '__dict__' of 'Person' objects>), ('__doc__', None), ('__init__', <function Person.__init__ at 0x000002A2221BB790>), ('__module__', '__main__'), ('__weakref__', <attribute '__weakref__' of 'Person' objects>), ('age', 3)]
==================================================================
<class '__main__.Person'> <class '__main__.Person'>
[('name', 'Tom')]
==================================================================
Person
[('__dict__', <attribute '__dict__' of 'Person' objects>), ('__doc__', None), ('__init__', <function Person.__init__ at 0x000002A2221BB790>), ('__module__', '__main__'), ('__weakref__', <attribute '__weakref__' of 'Person' objects>), ('age', 3)]
[('__dict__', <attribute '__dict__' of 'Person' objects>), ('__doc__', None), ('__init__', <function Person.__init__ at 0x000002A2221BB790>), ('__module__', '__main__'), ('__weakref__', <attribute '__weakref__' of 'Person' objects>), ('age', 3)]
3.4 示例 2
class Person:
age = 3
height = 170
def __init__(self, name, age=18):
self.name = name
self.age = age
tom = Person('Tom')
jerry = Person('Jerry', 20)
Person.age = 30
print(Person.age, tom.age, jerry.age) # 30 18 20
print(Person.height, tom.height, jerry.height) # 170 170 170
print(jerry.__dict__) # {'name': 'Jerry', 'age': 20}
jerry.height = 175
print(Person.height, tom.height, jerry.height) # 170 170 175
print(jerry.__dict__) # {'name': 'Jerry', 'age': 20, 'height': 175}
tom.height += 10
print(Person.height, tom.height, jerry.height) # 170 180 175
Person.height += 15
print(Person.height, tom.height, jerry.height) # 185 180 175
Person.weight = 70
print(Person.weight, tom.weight, jerry.weight) # 70 70 70
print(tom.__dict__) # {'name': 'Tom', 'age': 18, 'height': 180}
print(tom.__class__.__dict__['weight']) # 70
print(tom.weight) # 70
# print(tom.__dict__['weight']) # KeyError: 'weight'
3.5 示例 3
class MyClass:
def normal_method():
print('Normal Method')
MyClass.normal_method() # Normal Method
# MyClass().normal_method() # normal_method() takes 0 positional arguments but 1 was given
# 由于 normal_method 在定义的时候没有指定 self,所以不能完成实例对象的绑定
# 不能用 MyClass().normal_method() 调用
# 严禁这么使用,做实验只是为了加深大家对 类 的理解
4、类方法 和 静态方法
4.1 类方法
@classmethod
装饰器修饰的方法cls
,cls
指代调用者即类对象自身cls
这个标识符可以是任意合法名称,但是为了易读,请不要修改cls
可以直接操作类的属性cls
操作类的实例4.2 静态方法
@staticmethod
装饰器修饰的方法4.3 总结
4.4 示例 1
class Person:
def method(self):
print("{}'s method".format(self))
@classmethod
def class_method(cls):
print('class = {0.__name__}({0})'.format(cls))
cls.HEIGHT = 170
@staticmethod
def static_method():
print(Person.HEIGHT)
# 类访问
# print(Person.method()) # method() missing 1 required positional argument: 'self'
print(Person.class_method()) # class = Person(<class '__main__.Person'>) None
print(Person.static_method()) # 170 None
print(Person.__dict__)
# {'__module__': '__main__', 'method': <function Person.method at 0x000002A2220C34C0>, 'class_method': <classmethod object at 0x000002A22294BB80>, 'static_method': <staticmethod object at 0x000002A22294B340>, '__dict__': <attribute '__dict__' of 'Person' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__doc__': None, 'HEIGHT': 170}
tom = Person()
print(tom.method()) # <__main__.Person object at 0x000002A222314970>'s method None
print(tom.class_method()) # class = Person(<class '__main__.Person'>) None
print(tom.static_method()) # 170 None
5、访问控制
Python
解释器会将其改名,转换名称为 _类名__变量名
的名称。【注意,隐藏属性改名的只和当前类有关系,设置私有属性的当前类,在谁里面改谁的名】
Python
中使用单 双下划线来标识一个成员被保护或者被私有化隐藏起来,不要万不得已,不要修改5.1 示例 1
class Person:
def __init__(self, name, age=10):
self.name = name
self.__age = age
def growup(self, i=1):
if i > 0 and i < 100:
self.__age += i
def showage(self):
return self.__age
p1 = Person('Tom')
print(p1.__dict__) # {'name': 'Tom', '_Person__age': 10}
p1.growup(55)
print(p1.__dict__) # {'name': 'Tom', '_Person__age': 65}
# print(p1.__age) # 'Person' object has no attribute '__age'
print(p1._Person__age) # 65
print(p1.showage()) # 65 这是正确访问隐藏属性的方法
p1.__age = 100
print(p1.showage()) # 65
print(p1.__age) # 100
print(p1.__dict__) # {'name': 'Tom', '_Person__age': 65, '__age': 100}
5.2 示例 2
class Person:
def __init__(self, name, age=18):
self.name = name
self._age = age
tom = Person('Tom')
# print(tom.age) # 'Person' object has no attribute 'age'
print(tom.__dict__) # {'name': 'Tom', '_age': 18}
print(tom._age) # 18
6、属性装饰器
property
装饰器的时候,方法要同名. + 属性名称
进行访问property
装饰器:后面跟的函数名就是以后的属性名,它就是getter
,这个必须有,有了它至少是只读属性setter
装饰器:与属性名同名,且接收两个参数,第一个是self
,第二个是将要赋值的值,有了它,属性可写deleter
装饰器:可以控制是否删除属性,很少用property
装饰器必须在前,setter deleter
装饰器在后property
装饰器能通过简单的方式,把对方法的操作变成对属性的访问,并起到了一定隐藏结果class A:
def test(self):
print('test 1')
A().test()
# test 1
class A:
@property
def test(self):
print('test 1')
# A().test() # 'NoneType' object is not callable
A().test
# test 1
6.1 示例 1
class Person:
def __init__(self, name, age=18):
self.name = name
self.__age = age
@property
def age(self):
print('getter')
return self.__age
@age.setter
def age(self, age):
print('setter')
self.__age = age
@age.deleter
def age(self):
print('deleter')
del self.__age
tom = Person('Tom')
print(tom.age) # getter 18
tom.age = 22 # setter
print(tom.age) # getter 18
print(tom.__dict__) # {'name': 'Tom', '_Person__age': 22}
del tom.age # deleter
print(tom.__dict__) # {'name': 'Tom'}
6.2 示例 2
class Person:
def __init__(self, name, age=18):
self.name = name
self.__age = age
def getage(self):
print('getter')
return self.__age
def setage(self, age):
print('setter')
self.__age = age
def delage(self):
print('deleter')
del self.__age
age = property(getage, setage, delage, 'age property')
tom = Person('Tom')
print(tom.age) # getter 18
tom.age = 22 # setter
print(tom.age) # getter 18
print(tom.__dict__) # {'name': 'Tom', '_Person__age': 22}
del tom.age # deleter
print(tom.__dict__) # {'name': 'Tom'}
7、对象的销毁
__del__
方法,称为析构函数(方法)。del
语句删除实例,引用计数减1,当引用计数为0时,会自动调用 __del__
方法。Python
实现了垃圾回收机制,不能确定对象何时执行垃圾回收。__del__
方法,除非你明确知道自己的目的,否则不要手动调用这个方法import time
class Person:
def __init__(self, name, age=18):
self.name = name
self.__age = age
def __del__(self):
print('delete {}'.format(self.name))
tom = Person('tom')
tom.__del__()
tom.__del__()
tom.name
tom2 = tom
tom3 = tom2
print(tom, tom2, tom3)
del tom
time.sleep(3)
del tom2
print('=' * 33)
del tom3
delete tom
delete tom
<__main__.Person object at 0x000002A221A59F10> <__main__.Person object at 0x000002A221A59F10> <__main__.Person object at 0x000002A221A59F10>
=================================
delete tom
8、__init__
与 __del__
构造方法__init__
,具有初始化的作用,也就是当该类被实例化的时候就会自动执行该函数。那么通常就可以把要先初始化的属性放到这个方法里面析构方法__del__
是对象在被垃圾回收的时候起作用的一个方法,它的执行一般也就意味着对象不能够继续引用, 回收内存8.1 示例 1
运行脚本结束后,系统会自动执行析构函数,所以 ‘del method’
晚于 'The last line code in the test5.py'
打印。
class Person:
def __init__(self):
print('init method')
def __del__(self):
print('del method')
p = Person()
print('The last line code in the test5.py')
D:\PycharmProjects\pythonProject\202108089\venv\Scripts\python.exe D:/PycharmProjects/pythonProject/202108089/test5.py
init method
The last line code in the test5.py
del method
Process finished with exit code 0
8.2 示例 2
del
语句执行时,内存立即被回收,所以 ‘del method’
早于 'The last line code in the test6.py'
打印
class Person:
def __init__(self):
print('init method')
def __del__(self):
print('del method')
p = Person()
del p
print('The last line code in the test6.py')
D:\PycharmProjects\pythonProject\202108089\venv\Scripts\python.exe D:/PycharmProjects/pythonProject/202108089/test6.py
init method
del method
The last line code in the test6.py
Process finished with exit code 0
9、Class
总结
类是抽象的,对象是具体的,类就是属性和方法的集合。属性 一般使用数据结构来保存,方法 操作或者动作或者能力
类对象是由type
构造出来的对象,其实类对象也是一个实例,由type
创造出来的实例(涉及到元编程,后续在更新)
class MyClass:
pass
A = MyClass()
type(MyClass), type(A) # (type, __main__.MyClass)
type(int), type(str) # (type, type)
# int str 都是类对象,自定义类请全使用大驼峰命名方式
类属性和类变量 可以使用 .
来进行获取,每次实例化都会产生不同的对象,单例模式除外
类方法:不管是实例调用,还是类调用,都会自动把类注入为第一参数;换言之,第一个参数永远是cls,如果写为别的参数名称或者不写参数时,实际运行时,也会把类作为第一个参数
静态方法:可以不需要任何参数,类和实例都可以调用;如果有类似需求,可以使用静态方法(静态方法装饰器会阻止任何参数注入
)
普通的类方法(带self
,也就是有参):如果有参数的话,第一个参数肯定是self
,如果写为别的参数名称,实际运行时,如果是实例,会注入实例本身self
作为参数;如果是类调用,则是任何参数就行;因为实例有绑定效果,类没有绑定;普通的类方法(不带self
,也就是无参):语法可以,但是禁止这么使用,如果有无参方法需求的话,请使用静态方法
为了大家便于查阅代码,self cls
等参数,大家不要随意替换。
class MyClass:
def showmyclass(self):
print(id(self))
A = MyClass()
B = MyClass()
MyClass.showmyclass
# <function __main__.MyClass.showmyclass(self)>
A.showmyclass # 实例对象和self绑定在一起,在调用时,是不需要输入self参数
# <bound method MyClass.showmyclass of <__main__.MyClass object at 0x0000017DB2795DF0>>
# MyClass.showmyclass() # showmyclass() missing 1 required positional argument: 'self'
A.showmyclass() # 1639376838128
MyClass.showmyclass(A) # 1639376838128
来源:Lee木木