【问题标题】:What's an example use case for a Python classmethod?Python 类方法的示例用例是什么?
【发布时间】:2011-08-09 22:56:45
【问题描述】:

我已阅读 What are Class methods in Python for?,但该帖子中的示例很复杂。我正在为 Python 中的类方法的特定用例寻找一个清晰、简单、简单的示例。

您能否列举一个小的、具体的示例用例,其中 Python 类方法将成为该工作的正确工具?

【问题讨论】:

  • 我已经编辑了这个问题来解释这两个问题之间的区别。我正在寻找一个 Hello World 类型的裸骨示例用例。

标签: python class class-method


【解决方案1】:

用于初始化的辅助方法:

class MyStream(object):

    @classmethod
    def from_file(cls, filepath, ignore_comments=False):    
        with open(filepath, 'r') as fileobj:
            for obj in cls(fileobj, ignore_comments):
                yield obj

    @classmethod
    def from_socket(cls, socket, ignore_comments=False):
        raise NotImplemented # Placeholder until implemented

    def __init__(self, iterable, ignore_comments=False):
       ...

【讨论】:

  • 确实,提供替代构造函数是classmethod 的经典用例。与它们的 staticmethod 等价物不同,它们可以很好地处理子类。
【解决方案2】:

__new__ 是一个非常重要的类方法。这是实例通常来自的地方

所以dict() 当然会调用dict.__new__,但有时还有另一种方便的方法来制作dicts,即classmethod dict.fromkeys()

例如。

>>> dict.fromkeys("12345")
{'1': None, '3': None, '2': None, '5': None, '4': None}

【讨论】:

  • +1 是一个很好的真实示例。尽管您隐含了 fromkeys 也是一个类方法这一事实。
  • 从技术上讲,__new__ 被隐式转换为静态方法。在实例创建过程中还有其他机制将适当的类对象作为第一个参数传入。 dict.fromkeys 是实际类方法的一个很好的例子。
  • 当然,__new__ 仍然依赖于与classmethod 相同的概念,即使它实际上并未以这种方式实现。
【解决方案3】:

我不知道,类似于命名构造方法?

class UniqueIdentifier(object):

    value = 0

    def __init__(self, name):
        self.name = name

    @classmethod
    def produce(cls):
        instance = cls(cls.value)
        cls.value += 1
        return instance

class FunkyUniqueIdentifier(UniqueIdentifier):

    @classmethod
    def produce(cls):
        instance = super(FunkyUniqueIdentifier, cls).produce()
        instance.name = "Funky %s" % instance.name
        return instance

用法:

>>> x = UniqueIdentifier.produce()
>>> y = FunkyUniqueIdentifier.produce()
>>> x.name
0
>>> y.name
Funky 1

【讨论】:

    【解决方案4】:

    使用@classmethod 的最大原因是在旨在被继承的备用构造函数中。这在多态性中非常有用。一个例子:

    class Shape(object):
        # this is an abstract class that is primarily used for inheritance defaults
        # here is where you would define classmethods that can be overridden by inherited classes
        @classmethod
        def from_square(cls, square):
            # return a default instance of cls
            return cls()
    

    注意Shape 是一个抽象类,它定义了一个类方法from_square,因为Shape 没有真正定义,它并不真正知道如何从Square 派生自己,所以它只是返回一个默认实例类的。

    然后允许继承的类定义自己的此方法版本:

    class Square(Shape):
        def __init__(self, side=10):
            self.side = side
    
        @classmethod
        def from_square(cls, square):
            return cls(side=square.side)
    
    
    class Rectangle(Shape):
        def __init__(self, length=10, width=10):
            self.length = length
            self.width = width
    
        @classmethod
        def from_square(cls, square):
            return cls(length=square.side, width=square.side)
    
    
    class RightTriangle(Shape):
        def __init__(self, a=10, b=10):
            self.a = a
            self.b = b
            self.c = ((a*a) + (b*b))**(.5)
    
        @classmethod
        def from_square(cls, square):
            return cls(a=square.length, b=square.width)
    
    
    class Circle(Shape):
        def __init__(self, radius=10):
            self.radius = radius
    
        @classmethod
        def from_square(cls, square):
            return cls(radius=square.length/2)
    

    该用法允许您以多态方式处理所有这些未实例化的类

    square = Square(3)
    for polymorphic_class in (Square, Rectangle, RightTriangle, Circle):
        this_shape = polymorphic_class.from_square(square)
    

    您可能会说这一切都很好,但我为什么不能使用 @staticmethod 来完成同样的多态行为:

    class Circle(Shape):
        def __init__(self, radius=10):
            self.radius = radius
    
        @staticmethod
        def from_square(square):
            return Circle(radius=square.length/2)
    

    答案是你可以,但你没有得到继承的好处,因为Circle 必须在方法中显式调用。这意味着如果我从继承的类中调用它而不覆盖,我仍然会每次都得到Circle

    请注意,当我定义另一个实际上没有任何自定义 from_square 逻辑的形状类时会得到什么:

    class Hexagon(Shape):
        def __init__(self, side=10):
            self.side = side
    
        # note the absence of classmethod here, this will use from_square it inherits from shape
    

    您可以在此处保留 @classmethod 未定义,它将使用来自 Shape.from_square 的逻辑,同时保留 cls 是谁并返回适当的形状。

    square = Square(3)
    for polymorphic_class in (Square, Rectangle, RightTriangle, Circle, Hexagon):
        this_shape = polymorphic_class.from_square(square)
    

    【讨论】:

    • 您在 RightTriangle 类中有错字。 init 方法应该是 "init" 但你把它写成 "__init"
    • 启蒙不应该是这样的:square = Square(3) rect = Rectangle(15,5) rt = RightTriangle(5,15) cir = Circle(50) for polymorphic_class in (rect,square,cir,rt): this_shape = polymorphic_class.from_square(polymorphic_class) print("Initating Class ",type(this_shape).__name__)
    【解决方案5】:

    我发现我最常使用@classmethod 将一段代码与一个类相关联,以避免创建全局函数,用于我不需要类的实例来使用代码的情况。

    例如,我可能有一个数据结构,它仅在符合某种模式时才认为键有效。我可能想在课堂内外使用它。但是,我不想再创建另一个全局函数:

    def foo_key_is_valid(key):
        # code for determining validity here
        return valid
    

    我更愿意将此代码与其关联的类分组:

    class Foo(object):
    
        @classmethod
        def is_valid(cls, key):
            # code for determining validity here
            return valid
    
        def add_key(self, key, val):
            if not Foo.is_valid(key):
                raise ValueError()
            ..
    
    # lets me reuse that method without an instance, and signals that
    # the code is closely-associated with the Foo class
    Foo.is_valid('my key')
    

    【讨论】:

    • 类方法可以用类或实例调用,例如add_key Foo.is_valid(key) 可以也可以是self.is_valid(key) docs.python.org/library/functions.html#classmethod
    • 这个用例听起来更像是静态方法而不是类方法。如果你的方法没有使用 cls 参数,那么你应该使用 @staticmethod 来代替。
    【解决方案6】:
    in class MyClass(object):
        '''
        classdocs
        '''
        obj=0
        x=classmethod
        def __init__(self):
            '''
            Constructor
            '''
            self.nom='lamaizi'
            self.prenom='anas'
            self.age=21
            self.ville='Casablanca'
    if __name__:
        ob=MyClass()
        print(ob.nom)
        print(ob.prenom)
        print(ob.age)
        print(ob.ville)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-06-11
      • 2014-12-03
      • 1970-01-01
      • 1970-01-01
      • 2016-01-13
      • 1970-01-01
      • 2021-05-11
      • 1970-01-01
      相关资源
      最近更新 更多