Python面向对象之绑定方法和非绑定方法

发布时间 2024-01-11 16:51:15作者: Lea4ning

绑定方法与非绑定方法

【一】概要

  • 在 Python 中,绑定方法是指将类的实例与类的方法关联起来的过程。绑定方法包含类实例作为第一个参数,通常被称为 self。当通过实例调用类的方法时,Python 会自动传递实例作为第一个参数,这个过程就是方法绑定。

【二】常用方法

【1】绑定方法(动态方法)

  • 普通方法(即不使用任何装饰器的方法)以及使用 @classmethod 装饰器修饰的类方法都是绑定方法。它们都将实例作为第一个参数传递,并与实例绑定

    • 绑定给对象的方法
    class Person(object):
        name = 'user'
        
    	'''正常定义在类中的函数,就被称为绑定给对象的方法'''
        def func(self, meter):
            print(f"{self.name} 正在跑步 {meter} m ")
    
    • 绑定给类的方法@classmethod
    class Person(object):
        name = 'user'
    	
        '''通过装饰器classmethod装饰的方法,就称为类绑定方法'''
        @classmethod
        def func_class(cls):
            print("正在使用类绑定方法")
    

【2】非绑定方法(静态方法)@staticmethod

class Person(object):
    name = 'user'
	
    '''通过装饰器staticmethod装饰的方法,就称为非绑定方法'''
    @staticmethod
    def func(name, meter):
        '''静态方法就是一个在类中的普通函数'''
        print(f"{name} 跑了 {meter} 米")

【3】总结

class Person(object):
    data_attribute = '数据属性'

    def func(self):
        print("这是普通的方法,也可以称为绑定给对象的方法")

    @classmethod
    def func1(cls):
        print("这是绑定给类的方法")

    @staticmethod
    def func2():
        print('这是一个普通的函数')


print(Person.__dict__)
# {'__module__': '__main__',
# 'data_attribute': '数据属性',
# 'func': <function Person.func at 0x000002551C6627A0>,
# 'func1': <classmethod(<function Person.func1 at 0x000002551C6635B0>)>,
# 'func2': <staticmethod(<function Person.func2 at 0x000002551C941A20>)>,
# ……

【三】详解

【1】绑定方法(动态方法)

  • 普通方法(即不使用任何装饰器的方法)以及使用 @classmethod 装饰器修饰的类方法都是绑定方法。它们都将实例作为第一个参数传递,并与实例绑定

    • 绑定给对象的方法
    class Person(object):
        name = 'user'
    
        def func(self, meter):
            print(self)
            print(f"{self.name} 正在跑步 {meter} m ")
    
    
    '''绑定方法依赖与实例后的对象,只有当有实例的对象时才能使用'''
    # 当对象直接调用绑定方法
    p = Person()   # <__main__.Person object at 0x00000211BDEAB910>
    p.func(meter=100)   # # user 正在跑步 100 m
    
    
    # 当使用类调用绑定方法时
    # 会发现,需要我们传递第一个参数了,如果不传将会报错
    
    # Person.func(meter=100)
    # TypeError: Person.func() missing 1 required positional argument: 'self'
    Person.func(self=Person(), meter=200)
    # print(self)|输出>>>:<__main__.Person object at 0x00000297A2659720>
    # print(f"{self.name} 正在跑步 {meter} m ")|输出>>>:user 正在跑步 200 m
    
    
    '''我们可以注意到 当类调用绑定方法时,self我们如果传的不是对象,也并不会报错'''
    Person.func(self=Person, meter=200)  # Person不加小括号的含义也就是类
    # <class '__main__.Person'>   # 可以看到self的值已经变成了类,而不是对象了
    # user 正在跑步 200 m    # 输出是正常的
    
    '''这是因为在python中一切皆对象,所有类型都可以作为对象传值'''
    
    '''但是,需要注意,当你的方法中有需要调用对象中的属性或者类中的属性时,会因为找不到对应的值报错'''
    
    Person.func(self='字符串作为对象', meter=300)
    
    # AttributeError: 'str' object has no attribute 'name'
    
    '''所以,一般情况下,使用类调用绑定给对象的方法时,传递的对象值为类名加小括号【Person()】'''
    
    • 绑定给类的方法@classmethod
    class Person(object):
        name = 'user'
    
        def func(self, meter):
            print(self)
            print(f"{self.name} 正在跑步 {meter} m ")
    
        @classmethod
        def func_class(cls):
            print(cls)
            '''__name__ 的含义是获取到类名'''
            print(f"类 {cls.__name__} 正在使用类绑定方法")
    
    
    '''通过装饰器classmethod装饰的方法,就称为类绑定方法'''
    # 对于类绑定方法,self参数,也就变成了cls,代表着代替的是类
    # 类就可以直接调用
    Person.func_class()
    # <class '__main__.Person'>
    # Person 正在使用类绑定方法
    
    '''绑定给类的方法,对象也是可以正常使用'''
    p = Person()
    p.func_class()
    # <class '__main__.Person'>   # 因为该对象是通过类实例化得到的,程序会自动将所属的类作为参数传递给方法
    # Person 正在使用类绑定方法
    
    class Person(object):
        name = 'user'
    
        def func(self, meter):
            print(self)
            print(f"{self.name} 正在跑步 {meter} m ")
    
        @classmethod
        def func_class(cls):
            print(cls)
            '''__name__ 的含义是获取到类名'''
            print(f"类 {cls.__name__} 正在使用类绑定方法")
    
    '''类名后小括号中,可以放其他类,含义是继承,具体请看下一篇文章面向对象的三大特性'''
    class People(Person):
        # 继承就是继承父类的属性,包括函数属性(方法)和数据属性
        ...
    
    
    p = People()
    p.func_class()
    '''此处是为了展示__name__的用法'''
    # <class '__main__.People'>
    # 类 People 正在使用类绑定方法
    
    

【2】非绑定方法(静态方法)@staticmethod

class Person(object):
    name = 'user'

    @staticmethod
    def func(name, meter):
        '''静态方法就是一个在类中的普通函数'''
        print(f"{name} 跑了 {meter} 米")


'''静态方法的第一个参数并不会被当成self参数,而可以忽略'''
'''并且,对象可以直接调用,类也可以直接调用'''
p = Person()
p.func(name="user001", meter=100)
# user001 跑了 100 米
Person.func(name="user002", meter=500)
# user002 跑了 500 米
'''而万事万物都是有代价的,静态函数就无法直接通过self参数灵活的调用和传递参数'''

class Person(object):
    name = 'user'
    def __init__(self,age):
        self.age = age

    @staticmethod
    def func():
        '''如果需要调用类中的其他属性,就需要通过类名+属性名调用'''
        class_data = Person.name
        print(f"类中的数据属性{class_data},就不能直接灵活调用了")
        
        '''为什么不能灵活调用了?因为这样写就已经写死了,这样写就只能够调用类中属性,而不能够调用对象独有的属性了'''

        
    def normal_func(self):
        '''以绑定给对象方法举例'''
        print(f"可以调用类的属性{self.name}")
        print(f"也可以调用自己独有的属性{self.age}岁")



p = Person(age=18)
p.func()
p.normal_func()
# 输出:
# 类中的数据属性user,就不能直接灵活调用了
# 可以调用类的属性user
# 也可以调用自己独有的属性18岁

【3】Python中,一切皆对象

  • 当我们打开源码,不难发现,Python中的基本数据类型都是通过类定义的
  • 通过type函数也可以发现,<class 'list'>

image-20240110200151058

  • 类中定义的方法也就是我们可以通过【.】来调用的方法
  • 也有一些魔法方法,如__init__

image-20240110201541231

  • 当我们选择list() 强转数据类型的时候,其实python就是帮我们执行了实例化了一个list类下的对象,调用了__init__方法初始化。
  • 虽然底层代码由于是CPython导致我们无法看到底层代码,但也可知,python中的一切数据类型,其实都是类初始化出来的对象

“前路漫漫其修远兮,吾将上下而求索。”

当越来越往深处接触,越是发觉前人的智慧是多么强大,同样的方法又衍生出了这么多妙用!

【4】总结

class Person(object):
    name = 'user'

    def read(self):
        print(f"{self.name} 正在读书")

    @classmethod
    def write(cls):
        print(f"{cls.run()} 正在写作业")
        print("如果此处不传实例化的对象,那么如果需要调用某些在类中的数据属性,将会报错")

    @staticmethod
    def run():
        print(f"跑步,无法调用类中的数据属性和函数属性")

p = Person()
p.read()
p.write()
p.run()

Person.write()
Person.read(p)
# Person.read('str')
# AttributeError: 'str' object has no attribute 'name'
Person.run()

动态方法(实例方法)、类方法、静态方法的使用场景

  1. 动态方法(实例方法):

    • 绑定对象: 这些方法是绑定给对象的,即在调用时,会将调用它们的对象作为第一个参数传递给方法。这使得方法能够访问和操作对象的属性。

    • 使用场景: 当方法需要访问或修改实例的属性时,通常使用动态方法。这些方法可以访问对象的状态,并对其进行操作。

  2. 类方法:

    • 绑定到类: 类方法是绑定到类而不是实例的方法。它们接受类作为第一个参数,通常命名为 cls
    • 使用场景: 类方法通常用于对类级别的属性进行操作或在创建对象之前执行某些逻辑。当方法需要与整个类相关,而不是特定实例时,可以使用类方法。
  3. 静态方法:

    • 不绑定对象: 静态方法是类的一部分,但它们不绑定到实例。它们不能访问实例的属性,因为它们不接受 self 参数。它们只能访问类的属性,因为它们被绑定到类本身。
    • 使用场景: 当一个方法与类有关,但不需要访问或修改实例状态时,可以使用静态方法。静态方法在方法中不需要访问 self,因此可以用于执行与类相关的任务。

【5】实际案例

【5.1】类绑定方法:自动生成固定初始化信息的对象

'''自动生成对象'''


class MySql():
    def __init__(self, ip, port):
        self.ip = ip
        self.port = port

    @classmethod
    def make_obj(cls):
        return cls(ip='127.0.0.1', port=3306)


mysql_obj = MySql.make_obj()

【5.2】静态方法:生成工具类供外界调用

class Tools():
    '''当我们定义一个工具类,希望外界调用时,就可以使用静态方法'''

    @staticmethod
    # 生成随机ID
    def creat_id():
        '''函数内部并不依赖对象的属性或类的属性'''
        import uuid
        return uuid.uuid4()

    @staticmethod
    # 加密
    def encrypt_data(data):
        from hashlib import md5
        md5 = md5()
        md5.update(data.encode('utf8'))
        return md5.hexdigest()


random_id = Tools.creat_id()
print(random_id)   # b45cb0a0-7baa-4f7c-b84b-1437bf628070

encrypted_data = Tools.encrypt_data('hello')
print(encrypted_data)  # 5d41402abc4b2a76b9719d911017c592