一、类方法
类方法特点:
定义需要依赖装饰器@classmethod
类方法中参数不是一个对象,而是类 cls
类方法中只可以使用类属性
类方法中可否使用普通方法? 不能
类方法作用: 因为只能访问类属性和类方法,所以可以在对象创建之前,如果需要完成一些动作(功能)使用。
类方法是可以通过类或它的实例来调用的方法, 不管你是用类来调用这个方法还是类实例调用这个方法,类方法的第一个参数总是定义该方法的类对象。
class Dog:
def __init__(self, nickname):
self.nickname = nickname
def run(self): # self 对象
print('{}在院子里跑来跑去!'.format(self.nickname))
@classmethod
def test(cls): # cls是 class 的缩写
print(cls)
Dog.test()
# <class '__main__.Dog'>
类方法定义需要依赖装饰器@classmethod,且第一个参数为cls(是class)的缩写。打印 cls, 可以看到输出的是 Dog类,相当于 Dog 数据类型。
类方法中只能通过类名调用类空间中的类属性和类方法,不能调用实例属性和实例方法
class Dog:
def __init__(self, nickname):
self.nickname = nickname
def run(self): # self 对象
print('{}在院子里跑来跑去!'.format(self.nickname))
@classmethod
def test(cls): # cls class
print('----------')
print(cls) # <class '__main__.Dog'>
@classmethod
def test1(cls): # cls class
print(cls) # <class '__main__.Dog'>
print(self.nickname) # 报错 , 类中无(self) ,即 调用类方法时, 无对象
self.run()
print(cls.nickname) # 报错, 类中没有 nickname 属性,
Dog.test() # 运行成功:<class '__main__.Dog'>
# Dog.run() # 运行失败:run() missing 1 required positional argument: 'self'
# 创建一个Dog类对象:
dog = Dog('kit') # 创建对象时通过 init 初始化方法对 dog 对象赋了实例属性 nickname = 'kit'
Dog.test1() # 创建对象后,通过类名调用类方法对实例属性和实例方法进行操作, 报错
类方法对类属性和其它类方法进行访问与操作
对普通类属性 :
可以在类外进行访问修改: 类名.属性名 = 修改值
对私有类属性:
只能在类中访问, 在类外不能进行访问和修改。 此时,可使用 类方法 对私有属性进行操作修改
class Person:
__age = 18
def __init__(self, name):
self.name = name
def show(self):
print('--------->', Person.age)
@classmethod
def update_age(cls, age):
cls.__age = age
print('----------->类方法')
@classmethod
def show_age(cls):
print(cls.update_age(12))
print('修改后的年龄是:', cls.__age)
Person.show_age()
# Person.__age # type object 'Person' has no attribute '__age'
结果为:
----------->类方法
None # 输出 None 是因为update_age(),无 return返回值
修改后的年龄是: 12
理解:Person.show_age()通过类名调用了类方法show_age(),同时类方法show_age()中又调用了类方法update_age(),类方法update_age()对私有类属性__age = 18进行了修改。
总结:可以通过类名调用类方法和普通私有类属性,在类方法中可以访问任意状态的类属性(包括不能通过类名直接访问的私有类属性)和其它类方法。
二、静态方法
静态方法的特点(很类似类方法):
1. 需要装饰器@staticmethod
2. 静态方法是无需传递参数(cls,self)
静态方法的作用:
不需要定义实例即可使用这个方法。另外,多个实例共享此静态方法。常用来对类中的(类名+类属性或者类方法进行打印)
注:无法访问类属性和实例属性及其方法(可以从静态方法不需要self,cls参数可知)
class Person:
__age = 16
def __init__(self, name):
self.name = name
@classmethod
def show_age(cls):
cls.__age = 20
print('修改后的年龄是:', cls.__age)
@staticmethod
def test():
print('静态方法test()成功!')
print(Person.__age)
print(Person.show_age())
Person.test()
结果为:
静态方法test()成功!
16
修改后的年龄是: 20
由上述代码可知test()静态方法,可以打印输出__age私有类属性。
总结:
类方法 静态方法
不同:
- 装饰器不同
- 类方法是有参数的,静态方法没有参数
相同:
- 只能访问类的属性和方法(类方法必须通过cls参数才能访问,静态方法必须通过类名才能访问),对象的是无法访问的
- 都可以通过类名调用访问
- 都可以在创建对象之前使用,因为是不依赖于对象
普通方法 与 两者区别:
不同:
- 没有装饰器
- 普通方法永远是要依赖对象,因为每个普通方法都有一个self
- 只有创建了对象才可以调用普通方法,否则无法调用。
三、 魔术方法
在Python中,所有以“__”双下划线包起来的方法,都统称为“Magic Method”(魔术方法),例如类的初始化方法 init ,Python中所有的魔术方法均在官方文档中有相应描述。
(一)构造和初始化方法
__init__我们很熟悉了,它在对象初始化的时候调用,我们一般将它理解为"构造函数".
class A(object):
def __init__(self,*args, **kwargs):
print ("init %s" %self.__class__)
def __new__(cls,*args, **kwargs):
print ("new %s" %cls)
return object.__new__(cls, *args, **kwargs)
a = A()
结果为:
new <class '__main__.A'>
init <class '__main__.A'>
从结果可以看出,当实例化A类时,”_ _new _ _ “方法首先被调用,然后是” _ _ init _ _”方法。
一般来说,”_ _ init _ _ “和” _ _ new_ _”函数都会有下面的形式:
def __init__(self, *args, **kwargs):
# func_suite
def __new__(cls, *args, **kwargs):
# func_suite
return obj
(1)“_ _new _ _ “方法在Python中是真正的构造方法(创建并返回实例),通过这个方法可以产生一个”cls”对应的实例对象,所以说” _ _ new _ ”方法一定要有返回
实例化对象是Object类底层实现,其他类继承了Object的__new__才能够实现实例化对象。
(2)对于” _ init _ _ “方法,是一个初始化的方法,“self”代表由类产生出来的实例对象,” _ _ init _ _”将对这个对象进行相应的初始化操作。
class A(object):
def __init__(self,*args, **kwargs):
print ("init %s" %self.__class__)
print(self)
def __new__(cls,*args, **kwargs):
print ("new %s" %cls)
position = object.__new__(cls)
print(position)
return position #
a = A()
结果为:
new <class '__main__.A'>
<__main__.A object at 0x0000025669FB5B50>
init <class '__main__.A'>
<__main__.A object at 0x0000025669FB5B50>
个人理解:
(1)“_ new _ _ “方法返回实例的地址,此时在类中寻找” _ init _ _ “方法,
如果没有则 把申请的内存地址 赋给对象名 ;
若有则 执行__init__方法,__init__方法必定有 self参数 ,此时 self参数代表的是 对象名 申请的地址;
(2)执行完__init__方法后, 地址赋给 对象名。此时对象名对应的空间已经存储了 __init__方法操作的各项值。
参考博客:
https://blog.csdn.net/qq_19707521/article/details/79359858
(二)del()
在对象的生命周期结束时, __del__会被调用,可以将__del__理解为"析构函数".
__del__定义的是当一个对象进行垃圾回收时候的行为。一般不需要重写
有一点容易被人误解, 实际上,x.del() 并不是对于del x的实现,但是往往执行del x时会调用x.del().
理解与测试以下几个概念:
del:
-
对象赋值
p = Person()
p1 =p
说明: p和p1共同指向同一个地址 -
删除地址的引用
del p1 删除p1对地址的引用 -
查看对地址的引用次数:
import sys
sys.getrefcount§ -
当一块空间没有了任何引用,默认执行__del__
ref =0
class Person:
def __init__(self, name):
self.name = name
def __del__(self):
print('------del---------')
p = Person('jack')
p1 = p # p1 指向 p 所在的地址
p2 = p # p2 指向 p 所在的地址
print(p1.name)
print(p2.name)
p1.name = 'tom' # 通过 p1 修改 p所在内存空间 的值
print(p.name)
print(p2.name) # tom
结果为;
jack
jack
tom
tom
2.删除地址的引用和删除对象的不同
import sys
class Person:
def __init__(self, name):
self.name = name
def __del__(self):
print('------del---------')
p = Person('jack')
p1 = p
p2 = p
print(sys.getrefcount(p)) # 4
del p2
print('删除p2后打印:', p.name)
print(sys.getrefcount(p)) # 3 # 查看对象指向的地址
del p1
print('删除p1后打印:', p.name)
print(sys.getrefcount(p)) # 2
del p
结果为;
4
删除p2后打印: jack
3
删除p1后打印: jack
2
------del---------
如上可知,‘ p = Person(‘jack’) p1 = p’ 是对p申请了一个内存空间,同时 p1指向p所对应的内存空间。 del p1 未调用__del__()方法,只是删除了地址的引用,当 del p 时,删除了对象p,其申请的空间没有了任何引用,默认执行__del__。
总结__del__()方法,只有在对象空间无任何引用时才自动调用。
(三)str()
在 Python 中,使用 print 输出 对象变量,默认情况下,会输出这个变量的类型,以及 在内存中的地址(十六进制表示)
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
p = Person('tom', 18)
print(p)
# <__main__.Person object at 0x000001860D56C6D0>
如果在开发中,希望使用 print 输出 对象变量 时,能够打印 自定义的内容,就可以利用 str 这个内置方法了
注意:str 方法必须返回一个字符串
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
return '姓名是:' + self.name + ',年龄:' + str(self.age)
p = Person('tom', 18)
print(p)
# 姓名是:tom,年龄:18
关于python面向对象的特性:封装,继承,多态 将在下篇写到。
以上理解:为个人从老师课堂讲解获得,暂时未查到文献,可能有部分错误。