1、基本概念
1.1 类 class
- 类是抽象的概念,是万事万物的抽象,是一类事物的共同特征的集合。
- 用计算机的语言描述类,是属性和方法的集合。
1.2 对象 instance / object
- 对象是类的具象,是一个实体。
- 对于我们每个人这个个体,都是抽象概念人类的不同的实体。
- 对象属性:它是对象状态的抽象,用数据结构来描述
- 对象操作:它是对象行为的抽象,用操作名和实现该操作的方法来描述
1.3 面向对象三要素
1.4 哲学思想
- 一切皆对象
- 对象是数据和操作的封装
- 对象是独立的,但是对象之间可以相互作用
- 目前 OOP(Object-oriented programming) 是最接近人类认知的编程范式
2、类
2.1 类的定义
- 1 必须使用 class 关键字
- 2 类名必须是用 大驼峰 命名
- 3 类定义完成后,就产生了一个类对象,绑定到了标识符 ClassName 上
class ClassName:
语句块
2.2 类对象及类属性
- 1 类对象,类的定义执行后会生成一个类对象
- 2 类的属性,类定义中的变量和类中定义的方法都是类的属性
- 3 类变量,直接定义再类中的属性;一般来讲,类变量可使用全大写来命名
2.3 类的实例化
- 1 在类对象名称后面加上一个括号,就是调用类的实例化方法,完成实例化
- 2 实例化就是真正的创建一个该类的对象(实例)
- 3 每次实例化后获得的实例,都是不同的实例
- 4 即使是使用同样的参数实例化,也得到不一样的对象
- 5
Python 类实例化后,会自动调用 __init__ 方法,这个方法的第一个形式参数必须留给 self ,其它参数随意
2.4 __init__ 方法
- 1 类的实例化,实际上就是调用的
__init__(self) 方法 - 2 这个方法可以不定义,如果没有定义会在实例化后隐式调用
- 3 作用:对实例进行初始化
- 4 初始化参数可以多个参数,但是第一个位置必须是
self - 5 此方法不能有返回值,也就是只能
return None - 6 实例化调用
__init__ 魔术方法来进行初始化-出厂配置,对生成的实例进行属性配置,__init__ 定义的是实例的属性
2.5 实例对象 instance
- 1 类实例化后一定会获得一个类的实例,就是 实例对象
- 2
__init__ 方法的第一参数 self 就是指代某一个实例自身 - 3 类实例化后,得到一个实例对象,调用类方法时,采用
instance.class_method() 的方式,实例对象就会绑定到方法上 - 4 调用类方法时,类方法的
self 参数,就是 实例自身 - 5
__init__ 中所定义的变量,是保存在 实例 上的,并不是 类 上,所以,称为 实例变量 - 6 实例变量是每一个实例自己的变量,是自己独有的
- 5 类变量是类的变量,是类的所有实例共享的属性和方法
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))
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
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)
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.__class__.__name__, type(Person).__name__
type(Person), Person.__class__
3.2 实例属性的查找顺序
- 1 实例使用
.点号 来访问属性,会先找自己的 __dict__ ,如果没有,再通过属性 __class__ 找到自己的类,再去类的 __dict__ 中找 - 2 如果实例使用
__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)
print(Person.height, tom.height, jerry.height)
print(jerry.__dict__)
jerry.height = 175
print(Person.height, tom.height, jerry.height)
print(jerry.__dict__)
tom.height += 10
print(Person.height, tom.height, jerry.height)
Person.height += 15
print(Person.height, tom.height, jerry.height)
Person.weight = 70
print(Person.weight, tom.weight, jerry.weight)
print(tom.__dict__)
print(tom.__class__.__dict__['weight'])
print(tom.weight)
3.5 示例 3
class MyClass:
def normal_method():
print('Normal Method')
MyClass.normal_method()
4、类方法 和 静态方法
4.1 类方法
- 1 在定义中,使用
@classmethod 装饰器修饰的方法 - 2 必须至少有一个参数,且第一个参数留给了
cls ,cls 指代调用者即类对象自身 - 3
cls 这个标识符可以是任意合法名称,但是为了易读,请不要修改 - 4 通过
cls 可以直接操作类的属性 - 5 无法通过
cls 操作类的实例
4.2 静态方法
- 1 在类定义中,使用
@staticmethod 装饰器修饰的方法 - 2 调用时,不会隐式的传入参数
- 3 静态方法,只是表明这个方法属于这个名词空间。函数归在一起,方便组织管理
4.3 总结
- 1 类几乎可以调用所有内部定义的方法,但是调用普通的方法 时会报错,原因是第一参数必须是类的实例
- 2 实例几乎也可以调用所有的方法,普通的函数的调用一般不可能出现,因为不允许这么定义
- 3 类除了普通方法都可以调用,普通方法需要对象的实例作为第一参数
- 4 实例可以调用所有类中定义的方法(包括类方法/静态方法),普通方法传入实例自身,静态方法和类方法需要找到实例的类
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.class_method())
print(Person.static_method())
print(Person.__dict__)
tom = Person()
print(tom.method())
print(tom.class_method())
print(tom.static_method())
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__)
p1.growup(55)
print(p1.__dict__)
print(p1._Person__age)
print(p1.showage())
p1.__age = 100
print(p1.showage())
print(p1.__age)
print(p1.__dict__)
5.2 示例 2
class Person:
def __init__(self, name, age=18):
self.name = name
self._age = age
tom = Person('Tom')
print(tom.__dict__)
print(tom._age)
6、属性装饰器
- 属性装饰器:使用
property 装饰器的时候,方法要同名 - 1
property 装饰器:后面跟的函数名就是以后的属性名,它就是getter ,这个必须有,有了它至少是只读属性 - 2
setter 装饰器:与属性名同名,且接收两个参数,第一个是self ,第二个是将要赋值的值,有了它,属性可写 - 3
deleter 装饰器:可以控制是否删除属性,很少用 - 4
property 装饰器必须在前,setter deleter 装饰器在后 - 5
property 装饰器能通过简单的方式,把对方法的操作变成对属性的访问,并起到了一定隐藏结果
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)
tom.age = 22
print(tom.age)
print(tom.__dict__)
del tom.age
print(tom.__dict__)
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)
tom.age = 22
print(tom.age)
print(tom.__dict__)
del tom.age
print(tom.__dict__)
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(int), type(str)
-
类属性和类变量 可以使用 . 来进行获取,每次实例化都会产生不同的对象,单例模式除外 -
类方法:不管是实例调用,还是类调用,都会自动把类注入为第一参数;换言之,第一个参数永远是cls,如果写为别的参数名称或者不写参数时,实际运行时,也会把类作为第一个参数 -
静态方法:可以不需要任何参数,类和实例都可以调用;如果有类似需求,可以使用静态方法(静态方法装饰器会阻止任何参数注入 ) -
普通的类方法(带self ,也就是有参):如果有参数的话,第一个参数肯定是self ,如果写为别的参数名称,实际运行时,如果是实例,会注入实例本身self 作为参数;如果是类调用,则是任何参数就行;因为实例有绑定效果,类没有绑定;普通的类方法(不带self ,也就是无参):语法可以,但是禁止这么使用,如果有无参方法需求的话,请使用静态方法 -
为了大家便于查阅代码,self cls 等参数,大家不要随意替换。
class MyClass:
def showmyclass(self):
print(id(self))
A = MyClass()
B = MyClass()
MyClass.showmyclass
A.showmyclass
A.showmyclass()
MyClass.showmyclass(A)
|