公众号:尤而小屋
作者:Peter
编辑:Peter
本文讲解一个python的基础知识点:Python中类和实例的讲解。主要包含:
基础类
首先创建一个类Person
class Person:
'''
this is a sample of class
'''
# 1 特殊方法--构造函数---初始化函数(方法)
def __init__(self, name):
self.name = name # 建立实例的属性name,传入的值恰好也是name
# 2
def get_name(self):
return self.name # 直接调用构造函数中类的属性
# 3
def color(self, color):
d = {}
d[self.name] = color # 调用类的属性(来自构造函数)
return d
python3中所有的类都是Object类的子类,且类名首字母需要大写。
1、 函数1是初始化方法:在用类创建实例时,首先需要执行初始化函数
2-3:两个函数的第一个参数必须是self,其他和普通函数的定义与使用完全相同
类是对象的定义,实例才是真实的物件:实例化一个Person对象
xiaoming = Person('xiaoming') # 实例化对象xiaoming
print('name of Person: ', xiaoming.name) # 实例的name属性
name = xiaoming.get_name() # 调用实例的方法get_name
print('get_name: ', name)
color = xiaoming.color('white') # 调用方法color,并传入参数white
print('color: ',color)
类的属性
定义一个简单的类:
class Person:
'''在这里我们创建一个非常简单的类Person'''
age = 20
其中,age就是Person类的属性。
1、查看属性
Person.age
2、修改属性的值
Person.age = 24
Person.age
3、查看属性相关信息
print(dir(Person))
结论:可以看到只有最后的age是创建时候自定义的,其他都是默认的。
# 以字典的形式返回所有属性
Person.__dict__
在这里我们可以看到在创建时候的注释文档:
# 显示类内部的注释说明文档
Person.__doc__
# 显示类的名字
Person.__name__
所有的类都是基于基类object:
# 显示类继承的基类object
Person.__base__
# 删除类的属性
del Person.age
再次运行则会报错:
# Person.age
创建实例
还是使用之前的Person类:
class Person:
'''
this is a sample of class
'''
# 1 特殊方法--构造函数---初始化函数(方法)
def __init__(self, name):
self.name = name # 建立实例的属性name,传入的值恰好也是name
# 2
def get_name(self):
return self.name # 直接调用构造函数中类的属性
# 3
def color(self, color):
d = {}
d[self.name] = color # 调用类的属性
return d
基于类创建实例
首先,我们创建一个实例:
boy = Person('xiaoming')
boy
语句boy = Person('xiaoming')
的本质是将变量boy
和Person('xiaoming')
建立引用关系。这种关系的确定和我们创建变量的赋值语句,比如a=2
是类似的。
其实,我们也可以看到:创建实例就是在调用类的过程。
type(boy)
__init__函数说明
在这里说明下:__init__()是一个特殊方法
- 在该方法里面主要是规定一些属性或者做一些初始化工作
__init__()
方法没有return语句,其实是return None,一般就不写
>>> class Foo:
... def __init__(self):
... print('I am __init__()')
... return 100
...
>>> foo = Foo()
I am __init__()
Traceback (most recent call last):
File '<stdin>', line 1, in <module>
TypeError: __init__() should return None, not 'int'
实例属性
上面我们创建了实例boy对象,它具有name属性:
boy.name
还可以修改实例的属性:
boy.name = 'lihua'
boy.name # 再次调用
除了自定义的name属性,实例还具有其他属性:
print(dir(boy)) # 最后的name属性
二者相互影响
实例属性可以随意修改,但是类的属性却不行:
class Foo:
x = 25
foo = Foo() # 类的实例化
foo.x # 实例的属性x
# 修改实例属性
foo.x = 32
foo.x
我们查看类的x属性,确实是没有变化的:
Foo.x
也就是说:实例属性的变化不会影响类的属性。
但是类的属性变化却能够影响实例属性的变化,因为实例本身就是通过调用类来建立的。
class Foo:
x = 25
foo = Foo() # 类的实例化
foo.x # 原实例属性值
Foo.x += 5 # Foo.x = Foo.x + 5
Foo.x
此时再查看实例的属性:
foo.x # 修改类的属性后的实例foo属性值
上面的例子中,类中的变量引用的是不可变对象,比如数值、字符串等。但是当类中引用的就是可变数据类型,比如列表等,则会不同:
class Foo:
x = [1,2,3,4]
# 类的属性
Foo.x
# 实例的属性
foo = Foo()
foo.x
修改实例的属性:
foo.x.append(55)
再次查看实例和类的属性:
foo.x
Foo.x
可以看到同时发生了变化。
修改类的属性:也会同时发生了变化。
Foo.x.append(66)
Foo.x
foo.x
self作用
在上面的多个例子中我们创建实例的时候,都没有用到self参数,它到底有什么用?
首先创建一个简单的类Person:
class Person:
def __init__(self,age):
self.age = age
#print('self:', self)
print('self的类型:',type(self))
age = Person(20) # 创建实例
age
print('实例age的类型:',type(age))
可以看到我们创建的实例age的类型和self的类型是一样的。可以理解成:self就是类Person的实例,self和age实例所引用的对象是一样的。
但是在类的外面不能这样使用:
# self.age
在Python类中,这个self参数必须显式地写,在类的内部是不能省略的,其他方法都必须是承接self实例对象。
比如下面的类中,3个方法中都使用了self作为第一个或者唯一参数:
class Person:
'''
this is a sample of class
'''
# 1 构造函数---初始化函数
def __init__(self, name, age, sex): # 通过self增加多个属性
self.name = name # 实例属性name
self.sex = sex # 实例属性sex
# 2
def age(self,n):
self.age = n # 属性age
# 3
def get_name(self):
return self.name # 直接调用构造函数中类的属性
# 4
def color(self, color):
d = {}
d[self.name] = color
return d
类的方法
在类中除了属性,其他就是方法,可以简单理解成函数,一个函数就是一个方法;还有其他的注释和文档(可忽略)。
首先理解下绑定方法和非绑定方法。先创建一个类:
class Foo: # 定义Foo类
def test(self):
print('this is a normal mthod of class!')
创建实例f:
f = Foo()
类和实例调用方法
通过实例调用方法:test()函数就是我们说的方法。在调用的时候self参数作为第一参数隐式的传递给了方法,在调用的时候无需再写。
self就是实例f。
f.test() # 调用方法
在调用方法的时候最后一定要加上括号;调用属性则是不需要的。
# 也可以显式把实例f地传递方法
Foo.test(f) # self 就是 f
如果类调用方法的时候不传递实例则会报错:
Foo.test()
TypeError Traceback (most recent call last)
~\AppData\Local\Temp\ipykernel_6936\659740599.py in <module>
----> 1 Foo.test()
TypeError: test() missing 1 required positional argument: 'self'
python中一切皆对象,类Foo的test()方法也是对象:函数对象。
非绑定方法
(1)通过类调用方法得到的是非绑定方法:
Foo.test # 对象
这种通过类的名字来调用方法对象,叫做:非绑定方法unbound method
。
绑定方法
(2)通过实例调用类的方法得到的是绑定方法
通过实例来得到方法对象:
f.test
可以看到结果中是绑定方法bound method
。
Foo.__dict__
在类的__dict__
属性中,可以看到:test方法就是一个function函数对象
Foo.__dict__['test'] # 获取字典的test键
描述器
python类中特殊的方法:__get__()、__set__()、__delete__()
,有这些方法的对象都叫做描述器。
关于描述器的使用:
descr.__get__(self, obj, type=None) --> value
descr.__set__(self, obj, value) --> None
descr.__delete__(self, obj) --> None
通过类的__dict__
属性调用__get__()
方法:
Foo.__dict__['test'].__get__(None, Foo) # 非绑定方法
对描述__get__的self赋值为None,表示没有给定的实例,得到是非绑定方法。等同于:
Foo.test # 非绑定方法
当我们给描述器传入实例对象f,得到的是绑定方法:
Foo.__dict__['test'].__get__(f, Foo) # 绑定方法
f.test # 绑定方法
- 通过类获取方法对象的时候,得到非绑定方法对象(继承相关知识用到)
类方法和静态方法
# 定义一个类
class Foo:
name = 'xiaoming' # 类属性
def __init__(self):
self.name = 'lisi' # 实例属性
def get_class_attr(cls):
return cls.name # 调用类的属性name
if __name__ == '__main__':
print('Foo.name:', Foo.name) # 类的属性
r = get_class_attr(Foo) # 传入类;得到类属性
print('get class attribute(cls.name):', r)
f = Foo() # 类的实例化;得到实例属性
print('instance attrbute:', f.name)
注意上面的结果:Foo.name就是类的属性,get_class_attr得到的也是类的属性,经过实例化后f.name得到的是实例的属性。
类方法
get_class_attr函数是写在类的外面,调用的是前面的Foo类对象,类和函数的耦合性太强,相互之间的影响很大,如何优化?将类和函数放在一起:使用装饰器。
# 定义一个类
class Foo:
name = 'xiaoming' # 类属性
def __init__(self):
self.name = 'lisi' # 实例属性
@classmethod # 类方法
def get_class_attr(cls): # 将类本身传入方法中
return cls.name # 直接调用类的属性name
if __name__ == '__main__':
print('Foo.name:', Foo.name) # 1-类属性
r = get_class_attr(Foo) # 2-传入类-得到类的属性
print('get class attribute:', r)
f = Foo()
print('instance attrbute:', f.name) # 3-类的实例化;得到实例的属性
print('get class attribute:', f.get_class_attr()) # 4-通过实例调用方法,仍然得到类属性
在2和4中,不管是通过类还是实例来执行get_class_attr方法,得到都是类属性的值。说明装饰器@classmethod所装饰的方法,传入的第一个参数不是self,这个和类中其他方法是不同的。
其参数引用的是类对象Foo。cls其实就是将类本身作为引用对象传入到装饰器方法中。
定义类方法:类里面定义的方法,比如上面的get_class_attr就是类方法。
静态方法
提供一份待优化的代码来理解静态方法:
# 待优化代码
import random
# 函数在外面,类中待调用
def select(n):
a = random.randint(1,100)
return a - n > 0
class Foo:
def __init__(self,name):
self.name = name
def get_name(self,age):
if select(age): # 调用上面定义的select函数
return self.name
else:
return 'name unknown!'
if __name__ == '__main__':
f = Foo('xiaoming')
age = f.get_name(25)
print(age)
经过优化后的代码:将原来类外面的函数放到类里面,加上装饰器@staticmethod;装饰器里面的函数和普通函数一样定义和使用。
# 待优化代码
import random
# 创建类
class Foo:
def __init__(self,name):
self.name = name
def get_name(self,age):
# 调用装饰器定义的select函数
if select(age): # 等价于self.select(age)
return self.name
else:
return 'name unknown!'
@staticmethod
def select(n):
a = random.randint(1,100)
return a - n > 0
if __name__ == '__main__':
f = Foo('xiaoming')
age = f.get_name(25)
print(age)
像这种使用@staticmethod
装饰器进行装饰的函数或者方法,我们称之为:静态方法。静态方法的第一个参数也不是self
。使用的时候可以通过实例self.select()
调用,也可以通过类调用Foo.select()
。
总结
(1)__init__函数
- 没有return语句;其实是return None
(2)self参数
- self就是类的实例;self和实例的引用对象是一样的
- 类中所有的方法的第一个参数必须是self,表示实例继承类的所有方法和属性
(3)绑定方法和非绑定方法
(4)类方法和静态方法
类方法:经过@classmethod装饰的方法,第一个参数不是self,一般用作cls,将类本身作为参数传入。类方法可以调用类中的其他属性或者方法供自身使用。
静态方法:经过@staticmethod装饰的方法,第一个参数也不是self,使用和普通函数相同。类中的其他方法可以直接实例调用装饰器下的方法,或者通过类调用。静态方法不能访问类中的其他属性或者方法。