__name__上的抽象属性未强制执行(Abstract Property on __name__ not enforced)
请考虑以下示例代码:
from abc import ABC, abstractmethod, abstractproperty class Base(ABC): @abstractmethod def foo(self) -> str: print("abstract") @property @abstractmethod def __name__(self) -> str: return "abstract" @abstractmethod def __str__(self) -> str: return "abstract" @property @abstractmethod def __add__(self, other) -> str: return "abstract" class Sub(Base): def foo(self): print("concrete") def __str__(self): return "concrete" def __add__(self, other) -> str: return "concrete" sub = Sub() sub.foo() sub.__name__ print(str(sub))
请注意,子类没有实现抽象属性
__name__
,实际上当引用__name__
时,它从父节点打印为“abstract”:>>> sub.foo() concrete >>> sub.__name__ 'abstract' >>> print(str(sub)) concrete
但是,这不是因为
__name__
是一个dunder方法,也不是因为__name__
和@abstractmethod
装饰器的某些问题不能很好地协同工作,因为如果我从Sub
删除__add__
的实现,它不会让我实例化它。 (我知道__add__
通常不是属性,但我想使用'真正的'dunder方法)如果删除__str__
和foo
的实现,会发生同样的预期行为。 只有__name__
这样。什么是
__name__
引起这种行为? 有没有办法解决这个问题,或者我是否需要手动为父(抽象)实现引发TypeError
?Consider the following sample code:
from abc import ABC, abstractmethod, abstractproperty class Base(ABC): @abstractmethod def foo(self) -> str: print("abstract") @property @abstractmethod def __name__(self) -> str: return "abstract" @abstractmethod def __str__(self) -> str: return "abstract" @property @abstractmethod def __add__(self, other) -> str: return "abstract" class Sub(Base): def foo(self): print("concrete") def __str__(self): return "concrete" def __add__(self, other) -> str: return "concrete" sub = Sub() sub.foo() sub.__name__ print(str(sub))
Note that the subclass does not implement the abstract property
__name__
, and indeed when__name__
is referenced, it prints as "abstract" from its parent:>>> sub.foo() concrete >>> sub.__name__ 'abstract' >>> print(str(sub)) concrete
However, it is not because
__name__
is a dunder method, nor because of some issue with@property
and@abstractmethod
decorators not working well together, because if I remove the implementation of__add__
fromSub
, it does not let me instantiate it. (I know__add__
is not normally a property, but I wanted to use a 'real' dunder method) The same expected behavior occurs if I remove the implementation of__str__
andfoo
. Only__name__
behaves this way.What is it about
__name__
that causes this behavior? Is there some way around this, or do I need to have the parent (abstract) implementation manually raise theTypeError
for me?
原文:https://stackoverflow.com/questions/41173879
满意答案
我们来看看生成的字节码:
fun <T> getValue(): T { return 1000 as T } // becomes public final getValue()Ljava/lang/Object; L0 LINENUMBER 17 L0 SIPUSH 1000 INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer; CHECKCAST java/lang/Object ARETURN L1 LOCALVARIABLE this LSimpleClass; L0 L1 0 MAXSTACK = 1 MAXLOCALS = 1
正如您所看到的,此方法不会将
1000
转换为Long
它只是确保对象的类型为java/lang/Object
(嗯,它是)并返回1000
作为Integer
对象。因此,您可以使用任何类型调用(注意:仅调用)此方法,这不会引发异常。 但是,将结果存储在变量中会调用实际强制转换,这可能会导致
ClassCastException
fun f3() { val simpleObject = SimpleClass() // L0 // LINENUMBER 16 L0 // NEW SimpleClass // DUP // INVOKESPECIAL SimpleClass.<init> ()V // ASTORE 0 simpleObject.getValue<SimpleClass>() // no problems // L1 // LINENUMBER 17 L1 // ALOAD 0 // INVOKEVIRTUAL SimpleClass.getValue ()Ljava/lang/Object; // POP val number = simpleObject.getValue<SimpleClass>() // throws ClassCastException1 // L2 // LINENUMBER 18 L2 // ALOAD 0 // INVOKEVIRTUAL SimpleClass.getValue ()Ljava/lang/Object; // CHECKCAST SimpleClass // ASTORE 1 // L3 // LINENUMBER 19 L3 // RETURN // L4 // LOCALVARIABLE number LSimpleClass; L3 L4 1 // LOCALVARIABLE simpleObject LSimpleClass; L1 L4 0 // MAXSTACK = 2 // MAXLOCALS = 2 }
但为什么将结果存储为
Long?
抛出异常? 再说一次,我们来看看字节码的差异:var number: Long? = null | var number: Long = 0 | ACONST_NULL | LCONST_0 CHECKCAST java/lang/Long | LSTORE 0 ASTORE 0 | number = simpleObject.getValue<Long>() [both] ALOAD 1 | INVOKEVIRTUAL SimpleClass.getValue ()Ljava/lang/Object; [both] CHECKCAST java/lang/Long | CHECKCAST java/lang/Number ASTORE 0 | INVOKEVIRTUAL java/lang/Number.longValue ()J | LSTORE 0
如您所见,
number: Long
的字节码将函数结果转换为Number
,然后调用Number.longValue
以将值转换为Long
(Java中的long
)但是,数字的字节码
number: Long?
将函数结果直接转换为Long?
(在Java中很Long
)导致ClassCastException
。不确定,如果这种行为记录在某处。 但是,
as
运算符执行不安全的转换,编译器会警告它:Warning:(21, 16) Kotlin: Unchecked cast: kotlin.Int to T
Let's take a look into generated bytecode:
fun <T> getValue(): T { return 1000 as T } // becomes public final getValue()Ljava/lang/Object; L0 LINENUMBER 17 L0 SIPUSH 1000 INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer; CHECKCAST java/lang/Object ARETURN L1 LOCALVARIABLE this LSimpleClass; L0 L1 0 MAXSTACK = 1 MAXLOCALS = 1
As you can see, this method does not cast
1000
to aLong
it simply ensures, that the object is of typejava/lang/Object
(well, it is) and returns the1000
as anInteger
object.Therefore, you may call (note: call only) this method with any type and this will not throw exception. However, storing the result in a variable invokes real cast which may lead to
ClassCastException
fun f3() { val simpleObject = SimpleClass() // L0 // LINENUMBER 16 L0 // NEW SimpleClass // DUP // INVOKESPECIAL SimpleClass.<init> ()V // ASTORE 0 simpleObject.getValue<SimpleClass>() // no problems // L1 // LINENUMBER 17 L1 // ALOAD 0 // INVOKEVIRTUAL SimpleClass.getValue ()Ljava/lang/Object; // POP val number = simpleObject.getValue<SimpleClass>() // throws ClassCastException1 // L2 // LINENUMBER 18 L2 // ALOAD 0 // INVOKEVIRTUAL SimpleClass.getValue ()Ljava/lang/Object; // CHECKCAST SimpleClass // ASTORE 1 // L3 // LINENUMBER 19 L3 // RETURN // L4 // LOCALVARIABLE number LSimpleClass; L3 L4 1 // LOCALVARIABLE simpleObject LSimpleClass; L1 L4 0 // MAXSTACK = 2 // MAXLOCALS = 2 }
But why storing the result as a
Long?
throws an exception? Again, let's take a look at the differences in bytecode:var number: Long? = null | var number: Long = 0 | ACONST_NULL | LCONST_0 CHECKCAST java/lang/Long | LSTORE 0 ASTORE 0 | number = simpleObject.getValue<Long>() [both] ALOAD 1 | INVOKEVIRTUAL SimpleClass.getValue ()Ljava/lang/Object; [both] CHECKCAST java/lang/Long | CHECKCAST java/lang/Number ASTORE 0 | INVOKEVIRTUAL java/lang/Number.longValue ()J | LSTORE 0
As you can see, the bytecode for
number: Long
casts the function result to aNumber
and then callsNumber.longValue
in order to convert the value to aLong
(long
in Java)However, the bytecode for
number: Long?
casts the function result directly into theLong?
(Long
in Java) which leads toClassCastException
.Not sure, if this behavior documented somewhere. However, the
as
operator performs unsafe cast and the compiler warns about it:Warning:(21, 16) Kotlin: Unchecked cast: kotlin.Int to T
相关问答
更多JSON在Java中加载:java.lang.ClassCastException:java.lang.Long无法强制转换为java.lang.Integer(JSON Loading in Java: java.lang.ClassCastException: java.lang.Long cannot be cast to java.lang.Integer)
ClassCastException:无法转换为java.lang.Integer(ClassCastException : cant be cast to java.lang.Integer)
java.util.ArrayList不能转换为java.lang.Long中的hibernate分离标准(java.util.ArrayList cannot be cast to java.lang.Long hibernate detached criteria)
java.lang.ClassCastException:java.math.BigInteger不能转换为java.lang.Long(java.lang.ClassCastException: java.math.BigInteger cannot be cast to java.lang.Long)
java:ClassCastException - [Ljava.lang.Long;(java: ClassCastException - [Ljava.lang.Long; cannot be cast to java.lang.Long)
java.lang.Integer不能在Kotlin中强制转换为java.lang.Long(当初始值为null时)(java.lang.Integer cannot be cast to java.lang.Long in Kotlin (when the initial value is null))
Hibernate HQL转换java.lang.ClassCastException:java.lang.Integer无法强制转换为java.lang.Long(Hibernate HQL casting java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.Long)
例外:java.lang.String无法强制转换为java.lang.Integer(Exception: java.lang.String cannot be cast to java.lang.Integer)
serverError:class java.lang.ClassCastException java.lang.Integer无法强制转换为java.lang.String(serverError: class java.lang.ClassCastException java.lang.Integer cannot be cast to java.lang.String)
Hibernate:java.lang.ClassCastException:java.lang.Integer无法强制转换为java.lang.Double(Hibernate : java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.Double)
相关文章
更多could not find system property or JNDI
mybatis There is no getter for property named 'xx' in 'class java.lang.String
spring+hibernate+mysql执行HQL查询报未打开游标错误
研磨设计模式之抽象工厂模式(Abstract Factory)-场景问题
eclipse+pydev环境下开发python的问题
页面获取ACTION的属性,页面不能弹出JS
抽象类与接口的区别
Java 抽象类
Java:IO/NIO篇,读写属性文件(properties)
java强制删除文件
最新问答
更多获取MVC 4使用的DisplayMode后缀(Get the DisplayMode Suffix being used by MVC 4)
如何通过引用返回对象?(How is returning an object by reference possible?)
矩阵如何存储在内存中?(How are matrices stored in memory?)
每个请求的Java新会话?(Java New Session For Each Request?)
css:浮动div中重叠的标题h1(css: overlapping headlines h1 in floated divs)
无论图像如何,Caffe预测同一类(Caffe predicts same class regardless of image)
xcode语法颜色编码解释?(xcode syntax color coding explained?)
在Access 2010 Runtime中使用Office 2000校对工具(Use Office 2000 proofing tools in Access 2010 Runtime)
从单独的Web主机将图像传输到服务器上(Getting images onto server from separate web host)
从旧版本复制文件并保留它们(旧/新版本)(Copy a file from old revision and keep both of them (old / new revision))
Copyright ©2023 peixunduo.com All Rights Reserved.粤ICP备14003112号
本站部分内容来源于互联网,仅供学习和参考使用,请莫用于商业用途。如有侵犯你的版权,请联系我们(neng862121861#163.com),本站将尽快处理。谢谢合作!