Python 构造时的类型检测和冲突避免

Python 构造时的类型检测和冲突避免,python,python-3.x,Python,Python 3.x,谢谢大家迄今为止的帮助。我把范围缩小了一点。如果您在脚本和类中查看这里,并运行脚本,您将看到发生了什么 添加行打印“789 789” 何时应该打印“456789” 似乎正在发生的事情是在new中,类正在检测传入参数的类型。但是,如果传入对象具有与构造函数相同的类型,则它似乎将传入对象分页到自身(在类级别),而不是返回旧对象。这是我能想到的唯一一件会导致456被涂成奶油的事情 那么,如何在构造函数中检测到与类类型相同的内容,并决定不将该数据分页到类内存空间,而是返回先前构造的对象呢 import

谢谢大家迄今为止的帮助。我把范围缩小了一点。如果您在脚本和类中查看这里,并运行脚本,您将看到发生了什么

添加行打印“789 789”

何时应该打印“456789”

似乎正在发生的事情是在new中,类正在检测传入参数的类型。但是,如果传入对象具有与构造函数相同的类型,则它似乎将传入对象分页到自身(在类级别),而不是返回旧对象。这是我能想到的唯一一件会导致456被涂成奶油的事情

那么,如何在构造函数中检测到与类类型相同的内容,并决定不将该数据分页到类内存空间,而是返回先前构造的对象呢

import sys
import math

class Foo(): 

    # class level property

    num = int(0) 

    # 
    # Python Instantiation Customs: 
    # 
    # Processing polymorphic input new() MUST return something or 
    # an object?,  but init() cannot return anything. During runtime 
    # __new__ is running at the class level, while init is running 
    # at the instance level. 
    # 

    def __new__(self,*arg): 

        print ("arg type: ", type(arg[0]).__name__)

###  functionally the same as isinstance() below
#
#       if (type(arg[0]).__name__) == "type": 
#           if arg[0].__name__ == "Foo":
#               print ("\tinput was a Foo")
#               return arg[0] # objects of same type intercede

### HERE <------------------------------------- 
# 
# this creams ALL instances, because since we are a class 
# the properties of the incoming object, seem to overwride 
# the class, rather than exist as a separate data structure. 

        if (isinstance(arg[0], Foo)): 
            print ("\tinput was a Foo")
            return arg[0] # objects of same type intercede

        elif (type(arg[0]).__name__) == "int": 
            print ("\tinput was an int")
            self.inum = int(arg[0]) # integers store
            return self

        elif (type(arg[0]).__name__) == "str": 
            print ("\tinput was a str")
            self.inum = int(arg[0]) # strings become integers
            return self

        return self 

    def __init__(self,*arg):
        pass

    # 
    # because if I can do collision avoidance, I can instantiate 
    # inside overloaded operators: 
    # 

    def __add__(self,*arg): 

        print ("add operator overload")

        # no argument returns self

        if not arg: 
            return self

        # add to None or zero return self

        if not arg[0]: 
            return self

        knowntype = Foo.Foo(arg[0])

        # add to unknown type returns False

        if not knowntype: 
            return knowntype

        # both values are calculable, calculate and return a Foo

        typedresult = (self.inum + knowntype.inum) 

        return Foo.Foo(typedresult) 

    def __str__(self): # return a stringified int or empty string

        # since integers don't have character length, 
        # this tests the value, not the existence of:  

        if self.inum: 
            return str(self.inum)

        # so the property could still be zero and we have to 
        # test again for no reason. 

        elif self.inum == 0:
            return str(self.inum)   

        # return an empty str if nothing is defined. 

        return str("")
导入系统 输入数学 类Foo(): #类级属性 num=int(0) # #Python实例化习惯: # #处理多态输入new()必须返回某些内容或 #对象?,但init()无法返回任何内容。运行时 #__new__在类级别运行,而init在运行 #在实例级别。 # 定义新(自我,*arg): 打印(“参数类型:”,类型(参数[0])。\uuuuu名称\uuuuu) ###功能与下面的isinstance()相同 # #如果(类型(参数[0])。\uuuuu name\uuuuuuu)=“类型”: #如果arg[0]。\uuuuu name\uuuuuu==“Foo”: #打印(“\t输入是一个Foo”) #返回arg[0]#相同类型的对象插入 ###这里 如何在构造函数时返回非新对象

通过重写构造函数方法而不是初始化器方法,
\uuuu init\uuuu

\uuuuuuuuuuuuuuuuuuuuuu
方法通常通过调用super的
\uuuuuuuuuuuuuuuuuuuuuuuuu
来构造一个实例,它最终到达
对象

\uuuu init\uuuu
方法得到了一个已由
\uuuu new\uuuu
构造的值,因此它现在不构造该值已经太迟了

请注意,如果
Foo.\uuuuuu new\uuuuu
返回一个
Foo
实例(无论是新创建的实例还是现有实例),将对其调用
Foo.\uuuuuuuu init\uuuu
。因此,重写
\uuuuu new\uuuuu
以返回对现有对象的引用的类通常需要一个幂等元
\uuuuuu init\uuuuuu
——通常,您根本不重写
\uuuuuu init\uuuuuu
,而是在
\uuuuuu new\uuuu
中进行所有初始化


这里有很多简单的
\uuuuuu new\uuuuuu
方法的例子,但让我们展示一个实际实现您所要求的简化版本的方法:

class Spam:
    _instances = {}
    def __new__(cls, value):
        if value not in cls._instances:
            cls._instances[value] = super().__new__(cls)
            cls._instances[value].value = value
        return cls._instances[value]
现在:

请注意,我确保使用
super
而不是
object
,以及
cls.\u实例
1而不是
Spam.\u实例
。因此:

>>> class Eggs(Spam):
...     pass
>>> e4 = Eggs(4)
>>> Spam(4)
<__main__.Eggs at 0x12650d208>
>>> Spam(4) is e4
True
>>> class Cheese(Spam):
...     _instances = {}
>>> c5 = Cheese(5)
>>> Spam(5)
<__main__.Spam at 0x126c28748>
>>> Spam(5) is c5
False
  • 如果这不能发生(例如,因为
    Foo
    是不可变的),则重写
    \uuuuu new\uuuu
  • 如果这正是用户所期望的(例如,因为
    Foo
    是某个对象的代理,该对象具有实际的
    spam
    ,并且同一对象的两个代理最好能看到相同的
    spam
    ),则可能会覆盖
    \uuuuuuuuuuu新的
  • 如果会让人困惑,可能不要覆盖
    \uuuu new\uuu
例如,使用classmethod:

>>> f1 = Foo.from_x(x)
>>> f2 = Foo.from_x(x)
…如果
f1是f2
结果为真,那么就不太可能令人惊讶了



一,。即使您将
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
定义为一个实例方法,并且它的主体看起来像一个类方法,但它实际上是一个静态方法,它通过您试图构造的类(它将是
Spam
Spam
的子类)作为一个普通的第一个参数,并带有构造函数之后就通过了。

感谢所有帮助过我的人!这个答案是为了理解如何重构已经编写好的现有程序,但存在可伸缩性问题。下面是完整的工作示例。它表明的是:

在给定用户定义和内置的传入类型的情况下,能够测试传入类型并在构造函数时避免不必要的对象复制。从重新定义的运算符或方法动态构造的能力。这些功能对于编写可扩展的可支持API代码是必不可少的。YMMV

福比

import sys
import math

class Foo(): 

    # class level property

    num = int(0) 

    # 
    # Python Instantiation Customs: 
    # 
    # Processing polymorphic input new() MUST return something or 
    # an object,  but init() MAYNOT return anything. During runtime 
    # __new__ is running at the class level, while __init__ is 
    # running at the instance level. 
    # 

    def __new__(cls,*arg): 

        print ("arg type: ", type(arg[0]).__name__)

        # since we are functioning at the class level, type() 
        # is reaching down into a non-public namespace, 
        # called "type" which is presumably something that 
        # all objects are ultimately derived from. 

        # functionally this is the same as isinstance() 

        if (type(arg[0]).__name__) == "Foo": 
            fooid = id(arg[0])
            print ("\tinput was a Foo: ", fooid)
            return arg[0] # objects of same type intercede

        # at the class level here, we are calling into super() for 
        # the constructor. This is presumably derived from the type() 
        # namespace, which when handed a classname, makes one of 
        # whatever it was asked for, rather than one of itself.  

        elif (type(arg[0]).__name__) == "int": 
            self = super().__new__(cls)
            self.inum = int(arg[0]) # integers store
            fooid = id(self)
            print ("\tinput was an int: ", fooid)
            return (self)

        elif (type(arg[0]).__name__) == "str": 
            self = super().__new__(cls)
            self.inum = int(arg[0]) # strings become integers
            fooid = id(self)
            print ("\tinput was a str: ", fooid)
            return (self)

#   def __init__(self,*arg):
#       pass

    # 
    # because if I can do collision avoidance, I can instantiate 
    # inside overloaded operators: 
    # 

    def __add__(self,*arg): 

        argtype = type(arg[0]).__name__

        print ("add overload in class:", self.__class__)

        if argtype == "Foo" or argtype == "str" or argtype == "int":   

            print ("\tfrom a supported type")

            # early exit for zero

            if not arg[0]: 
                return self

            # localized = Foo.Foo(arg[0])

            # FAILS: AttributeError: type object 'Foo' has no attribute 'Foo'
            # You can't call a constructor the same way from inside and outside


            localized = Foo(arg[0])

            print ("\tself class: ", self.__class__)
            print ("\tself number: ", self.inum)
            print ()
            print ("\tlocalized class: ", localized.__class__)
            print ("\tlocalized number: ", localized.inum)
            print ()

            answer = (self.inum + localized.inum) 
            answer = Foo(answer)    

            print ("\tanswer class:", answer.__class__)
            print ("\tanswer sum result:", answer.inum)

            return answer

        assert(0), "Foo: cannot add an unsupported type"

    def __str__(self): # return a stringified int or empty string

        # Allow the class to stringify as if it were an int. 

        if self.inum >= 0: 
            return str(self.inum)
testfoo.py

#! /usr/bin/python

import sys
import Foo 

# A python class is not transparent like in perl, it is an object 
# with unconditional inheritance forced on all instances that share 
# the same name. 

classhandle = Foo.Foo 

# The distinction between the special class object, and instance 
# objects is implicitly defined by whether there is a passed value at 
# constructor time. The following therefore does not work. 

# classhandle = Foo.Foo() 

# but we can still write and print from the class, and see it propagate, 
# without having any "object" memory allocated.  

print ("\nclasshandle: ", classhandle)
print ("classhandle classname: ", classhandle.__name__) # print the classname
print ("class level num: ", classhandle.num)     # print the default num
classhandle.classstring = "fdsa" # define an involuntary value for all instances

print ("\n")

# so now we can create some instances with passed properties. 

instance1 = Foo.Foo(int(123)) # 

print ("\ninstance1: ", instance1)
print ("involuntary property derived from special class memory space: ", instance1.classstring)
print ("instance property from int: ", instance1.inum)

print ("\n")

instance2 = Foo.Foo(str("456"))
print ("\ninstance2: ", instance2)
print ("instance2 property from int: ", instance2.inum)

# 
# instance3 stands for (shall we assume) some math that happened a 
# thousand lines ago in a class far far away. We REALLY don't 
# want to go chasing around to figure out what type it could possibly 
# be, because it could be polymorphic itself. Providing a black box so 
# that you don't have to do that, is after all, the whole point OOP. 
# 

print ("\npretend instance3 is unknowningly already a Foo\n")
instance3 = Foo.Foo(str("789"))

## So our class should be able to handle str,int,Foo types at constructor time. 

print ("\ninstance4 should be a handle to the same memory location as instance3\n")

instance4 = Foo.Foo(instance3) # SHOULD return instance3 on type collision

print ("instance4: ", instance4) 

# because if it does, we should be able to hand all kinds of garbage to 
# overloaded operators, and they should remain type safe.  

# since we are now different instances these are now different:  

print ("\nADDING:_____________________\n", instance2.inum, " ", instance4.inum)

instance5 = instance4 + int(549) # instance5 should be a Foo object. 
print ("\n\tAdd instance4, 549, instance5: ", instance4, " ", int(549), " ", instance5, "\n")

instance6 = instance2 + instance4 # also should be a Foo object
print ("\n\tAdd instance2, instance4, instance6: ", instance2, " ", instance4, " ", instance6, "\n")

print ("stringified instance6: ", str(instance6))

这可能是一个迂腐的观点,但我认为这很重要,“那些输入类型可能是原语或对象。”Python中没有原语类型。一切都是对象。“一切都是对象”:对我来说,这似乎是不正确的。在new(构造函数阶段?)中,type()找不到传递的结构的命名空间,但在init中找到了。在perl术语中,这意味着数据结构在新的中是不受约束的,而不是对象。这是一种被迫的习俗。据我所知,除了让一群人从一个共同的角度来看代码之外,没有理由这么做。我不明白你在这里说的是什么,以及它如何与Python中的对象相关。同样,没有基元类型。当您说“type()找不到所传递结构的命名空间”时,不清楚您的意思。什么样的结构?什么名称空间?名称空间实际上只是Python中的对象,通常(尽管不总是)
dict
对象。您似乎试图应用Pearl的概念,但这些概念并不适用于Python,在任何情况下,都没有“有福的和无福的”对象。在这里,有一个非常好的答案,描述了您相对于
\uuuuuuu new\uuuuuu
\uuuuu init\uuuuuuu>的错误。此外,这种构造
(type(arg[0])。\uuuu name\uuuuuu)==“int”
不是这样的
>>> f1 = Foo.from_x(x)
>>> f2 = Foo.from_x(x)
import sys
import math

class Foo(): 

    # class level property

    num = int(0) 

    # 
    # Python Instantiation Customs: 
    # 
    # Processing polymorphic input new() MUST return something or 
    # an object,  but init() MAYNOT return anything. During runtime 
    # __new__ is running at the class level, while __init__ is 
    # running at the instance level. 
    # 

    def __new__(cls,*arg): 

        print ("arg type: ", type(arg[0]).__name__)

        # since we are functioning at the class level, type() 
        # is reaching down into a non-public namespace, 
        # called "type" which is presumably something that 
        # all objects are ultimately derived from. 

        # functionally this is the same as isinstance() 

        if (type(arg[0]).__name__) == "Foo": 
            fooid = id(arg[0])
            print ("\tinput was a Foo: ", fooid)
            return arg[0] # objects of same type intercede

        # at the class level here, we are calling into super() for 
        # the constructor. This is presumably derived from the type() 
        # namespace, which when handed a classname, makes one of 
        # whatever it was asked for, rather than one of itself.  

        elif (type(arg[0]).__name__) == "int": 
            self = super().__new__(cls)
            self.inum = int(arg[0]) # integers store
            fooid = id(self)
            print ("\tinput was an int: ", fooid)
            return (self)

        elif (type(arg[0]).__name__) == "str": 
            self = super().__new__(cls)
            self.inum = int(arg[0]) # strings become integers
            fooid = id(self)
            print ("\tinput was a str: ", fooid)
            return (self)

#   def __init__(self,*arg):
#       pass

    # 
    # because if I can do collision avoidance, I can instantiate 
    # inside overloaded operators: 
    # 

    def __add__(self,*arg): 

        argtype = type(arg[0]).__name__

        print ("add overload in class:", self.__class__)

        if argtype == "Foo" or argtype == "str" or argtype == "int":   

            print ("\tfrom a supported type")

            # early exit for zero

            if not arg[0]: 
                return self

            # localized = Foo.Foo(arg[0])

            # FAILS: AttributeError: type object 'Foo' has no attribute 'Foo'
            # You can't call a constructor the same way from inside and outside


            localized = Foo(arg[0])

            print ("\tself class: ", self.__class__)
            print ("\tself number: ", self.inum)
            print ()
            print ("\tlocalized class: ", localized.__class__)
            print ("\tlocalized number: ", localized.inum)
            print ()

            answer = (self.inum + localized.inum) 
            answer = Foo(answer)    

            print ("\tanswer class:", answer.__class__)
            print ("\tanswer sum result:", answer.inum)

            return answer

        assert(0), "Foo: cannot add an unsupported type"

    def __str__(self): # return a stringified int or empty string

        # Allow the class to stringify as if it were an int. 

        if self.inum >= 0: 
            return str(self.inum)
#! /usr/bin/python

import sys
import Foo 

# A python class is not transparent like in perl, it is an object 
# with unconditional inheritance forced on all instances that share 
# the same name. 

classhandle = Foo.Foo 

# The distinction between the special class object, and instance 
# objects is implicitly defined by whether there is a passed value at 
# constructor time. The following therefore does not work. 

# classhandle = Foo.Foo() 

# but we can still write and print from the class, and see it propagate, 
# without having any "object" memory allocated.  

print ("\nclasshandle: ", classhandle)
print ("classhandle classname: ", classhandle.__name__) # print the classname
print ("class level num: ", classhandle.num)     # print the default num
classhandle.classstring = "fdsa" # define an involuntary value for all instances

print ("\n")

# so now we can create some instances with passed properties. 

instance1 = Foo.Foo(int(123)) # 

print ("\ninstance1: ", instance1)
print ("involuntary property derived from special class memory space: ", instance1.classstring)
print ("instance property from int: ", instance1.inum)

print ("\n")

instance2 = Foo.Foo(str("456"))
print ("\ninstance2: ", instance2)
print ("instance2 property from int: ", instance2.inum)

# 
# instance3 stands for (shall we assume) some math that happened a 
# thousand lines ago in a class far far away. We REALLY don't 
# want to go chasing around to figure out what type it could possibly 
# be, because it could be polymorphic itself. Providing a black box so 
# that you don't have to do that, is after all, the whole point OOP. 
# 

print ("\npretend instance3 is unknowningly already a Foo\n")
instance3 = Foo.Foo(str("789"))

## So our class should be able to handle str,int,Foo types at constructor time. 

print ("\ninstance4 should be a handle to the same memory location as instance3\n")

instance4 = Foo.Foo(instance3) # SHOULD return instance3 on type collision

print ("instance4: ", instance4) 

# because if it does, we should be able to hand all kinds of garbage to 
# overloaded operators, and they should remain type safe.  

# since we are now different instances these are now different:  

print ("\nADDING:_____________________\n", instance2.inum, " ", instance4.inum)

instance5 = instance4 + int(549) # instance5 should be a Foo object. 
print ("\n\tAdd instance4, 549, instance5: ", instance4, " ", int(549), " ", instance5, "\n")

instance6 = instance2 + instance4 # also should be a Foo object
print ("\n\tAdd instance2, instance4, instance6: ", instance2, " ", instance4, " ", instance6, "\n")

print ("stringified instance6: ", str(instance6))