python面向对象 —— 类中方法(二)

一、类方法

类方法特点:

定义需要依赖装饰器@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私有类属性。

总结:
类方法 静态方法
不同:

  1. 装饰器不同
  2. 类方法是有参数的,静态方法没有参数

相同:

  1. 只能访问类的属性和方法(类方法必须通过cls参数才能访问,静态方法必须通过类名才能访问),对象的是无法访问的
  2. 都可以通过类名调用访问
  3. 都可以在创建对象之前使用,因为是不依赖于对象

普通方法 与 两者区别:
不同:

  1. 没有装饰器
  2. 普通方法永远是要依赖对象,因为每个普通方法都有一个self
  3. 只有创建了对象才可以调用普通方法,否则无法调用。

三、 魔术方法

在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:

  1. 对象赋值
    p = Person()
    p1 =p
    说明: p和p1共同指向同一个地址

  2. 删除地址的引用
    del p1 删除p1对地址的引用

  3. 查看对地址的引用次数:
    import sys
    sys.getrefcount§

  4. 当一块空间没有了任何引用,默认执行__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面向对象的特性:封装,继承,多态 将在下篇写到。

以上理解:为个人从老师课堂讲解获得,暂时未查到文献,可能有部分错误。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

何为xl

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值