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