跳转至

绑定与非绑定方法

绑定方法

绑定给谁就应该由谁来 调用.谁来调用就会将谁当作第一个参数自动传入.

精髓在于 --- 自动传值

☆分类 self  cls

1>绑定给实例化对象的方法
     在类内部定义的函数(第一个参数约定俗成写为self+没有被任何装饰器修饰),通常默认就是绑定给对象用的.
     反过来理解,类中没有被装饰器绑定的函数,就是绑定给实例化对象用的,对象使用时,会自动将自己传入, 所以此函数需要有一个形参来接受, 此形参约定写成self.便于区分.
     有一些__开头的函数属于特殊情况... 特殊情况特殊分析

2>绑定给类的方法
     在类内部定义的函数(第一个参数约定俗成写为cls)若被装饰器@classmethod装饰器装饰,
     那么则是绑定给类的,应该由类来调用,会自动将类当作第一个参数自动传入

class Foo:

    @classmethod
    def f1(cls):
        print(cls)

    def f2(self):
        print(self)


obj = Foo()

# -- 绑定给类的
# ★ 了解:绑定给类的应该由类来调用,但实例化对象其实也可以使用,只不过自动传入的仍然是类.
print(Foo.f1)  # <bound method Foo.f1 of <class '__main__.Foo'>>
print(obj.f1)  # <bound method Foo.f1 of <class '__main__.Foo'>>
Foo.f1()  # <class '__main__.Foo'>
obj.f1()  # <class '__main__.Foo'>

# -- 绑定给实例化对象的
print(obj.f2)  # <bound method Foo.f2 of <__main__.Foo object at 0x7f90898498e0>>
print(Foo.f2)  # <function Foo.f2 at 0x7f9089848b80> 普通方法 该传多少参数就传多少参数
obj.f2()  # <__main__.Foo object at 0x7f90898498e0>
Foo.f2()  # 报错:f2() missing 1 required positional argument: 'self'

☆应用

若函数体代码需要用外部传入的类, 则应该将该函数定义成绑定给类的方法.
若函数体代码需要用外部传入的实例化对象, 则应该将该函数定义成绑定给对象的方法.

默认 的实例化方式: 类名( )
新的 实例化方式: 从配置文件中读取配置完成实例化!

"""
# -- setting.py
HOST = '127.0.0.1'
PORT = 3306
"""
import settings

class MySQL:
    def __init__(self, host, port):
        self.host = host
        self.port = port

    def tell_info(self):
        print(f'IP地址:{self.host},端口:{self.port}')

    @classmethod
    def from_conf(cls):
        print(cls)
        return cls(settings.HOST, settings.PORT)


# conn = MySQL('127.0.0.1',3306)
conn = MySQL.from_conf()
conn.tell_info()  # IP地址:127.0.0.1,端口:3306

非绑定方法

类中定义的函数如果被装饰器@staticmethod装饰,那么该函数就变成非绑定方法.

既不与类绑定,又不与对象绑定, 意味着类与对象都可以来调用 但无论谁来调用, 都 没有 任何 自动传值 的效果 .. 就是一个普通函数!!

@staticmethod

应用: 如果函数体代码既不需要外部传入的类也不需要外部传入的对象, 则应该将该函数定义成非绑定方法.

Q: 思考 => 不加@staticmethod,直接在类中定义, def func():pass 河狸吗?
A:[不合理] 还是老生常谈的一个问题,类中不加装饰器的函数默认是绑定给实例化对象使用的.
     实例化对象调用func, 发现func方法没有参数接收自动传入的对象自己.
     直接报错:func() takes 0 positional arguments but 1 was given

import uuid

class A:
    def __init__(self):
        self.uid = self.create_id()

    @staticmethod
    def func1(x, y):
        print('这是一个非绑定方法..')

    @staticmethod
    def create_id():
        return uuid.uuid1()

a = A()
print(a.func1)  # <function A.func1 at 0x7f806805b5e0>
print(A.func1)  # <function A.func1 at 0x7f806805b5e0>
a.func1(1, 2)  # 这是一个非绑定方法..
A.func1(1, 2)  # 这是一个非绑定方法..

"""
UUID是128位的全局唯一标识符,通常由32字节的字符串表示.它可以保证时间和空间的唯一性.

uuid1() 基于时间戳的算法
    由MAC地址、当前时间戳、随机数生成. 可以保证全球范围内的唯一性
    但MAC的使用同时带来安全性问题,局域网中可以使用IP来代替MAC

UUID主要有五个算法
首先,Python中没有基于DCE的,所以uuid2可以忽略;
其次,uuid4存在概率性重复,最好不用;
再次,若在Global的分布式计算环境下,最好用uuid1;
最后,若有名字的唯一性要求,最好用uuid3或uuid5.
"""

体会区别

PS: 体会classmethod与staticmethod的区别

我们的意图是想触发Mariadb.__str__,但是结果触发了MySQL.__str__的执行.
因为返回的m是MySQ类产生的... return MySQL(settings.HOST, settings.PORT)

import settings


class MySQL:

    def __init__(self, host, port):
        self.host = host
        self.port = port

    @staticmethod
    def from_conf():
        return MySQL(settings.HOST, settings.PORT)

    # @classmethod #哪个类来调用,就将哪个类当做第一个参数传入
    # def from_conf(cls):
    #     return cls(settings.HOST,settings.PORT) # 哪个类来调用,即用哪个类cls来实例化

    def __str__(self):
        return '就不告诉你'


class Mariadb(MySQL):

    # __str__方法需要返回一个字符串当做对这个实例化对象的描写
    def __str__(self):
        return '<%s:%s>' % (self.host, self.port)


m = Mariadb.from_conf()
print(m)  #我们的意图是想触发Mariadb.__str__,但是结果触发了MySQL.__str__的执行,打印就不告诉你:

总结绑定方法与非绑定方法的使用:
     若类中需要一个功能, 该功能的实现代码中需要引用对象则将其定义成对象方法;
     需要引用类则将其定义成类方法;
     无需引用类或对象则将其定义成静态方法.