473,320 Members | 1,699 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,320 software developers and data experts.

Behavioural identity - a short discussion

In mathematics two functions can be considered equal when they
represent the same function graph. This is nothing but a
set-theoretical identity. It is a nice criterion in theory bad a bad
one in practice because it is impossible to calculate all values of an
arbitrary function and this is true not only in practice but also in
theory. So mathematicians start to distinguish certain classes of
functions e.q. polynomials or power-series and prove identity theorems
about objects in those classes.

But what can be said about the equality of two arbitrary
Python-functions f and g? First of all not very much. If we define the
trivial functions

def f():pass
def g():pass

and we ask for equality the hash values will be compared and show us
that f and g are different.

On the other hand if we disassemble f and g we receive a completely
different picture:
dis.dis(f) 1 0 LOAD_CONST 0 (None)
3 RETURN_VALUE
dis.dis(g) 1 0 LOAD_CONST 0 (None)
3 RETURN_VALUE

This remains true if we add arguments to f:

def f(x):pass
dis.dis(f) 1 0 LOAD_CONST 0 (None)
3 RETURN_VALUE

Inspecting a function using dis.dis() enables us to speak about it's
"behavioural idenity".

What is it good for? Answer: for using implementations as interfaces.

Let's consider following classes:

class NotImplemented(Exception):
pass

class A(object):
def __init__(self):
raise NotImplemented

We can regard class A as a "pure abstract" class. It is impossible to
create instances of A. Each subclass of A that wants to be instantiated
must override __init__. This is clearly a property of A. But a client
object that inspects A by checking the availability of methods and
scanning argument signatures will remain insensitive to this simple
fact. Thinking in Python makes live easier because we can not only
check interfaces superficially but we can inspect the code and we can
compare two code-objects on the behavioural level with a certain
accuracy.

We start with a function

def not_implemented():
raise NotImplemented

and we are not interested in the name or in the argument-signature that
remains empty but in the implementation of not_implemented() as being
prototypical for other functions.

A variant of the dis.disassemble() function ( see [1]) deliveres:

['LOAD_GLOBAL', 'NotImplemented', 'RAISE_VARARGS', 'LOAD_CONST', None,
'RETURN_VALUE']

Analyzing A.__init__ will create exactly the same token stream. A
client object that compares the token streams of __init__ and
not_implemented holds a sufficient criterion for the abstractness of A.

[1] Implementation of a stripped down variant of the dis.disassemble()
function:

def distrace(co):
"trace a code object"
code = co.co_code
n = len(code)
i = 0
extended_arg = 0
free = None
while i < n:
c = code[i]
op = ord(c)
yield opname[op]
i = i+1
if op >= HAVE_ARGUMENT:
oparg = ord(code[i]) + ord(code[i+1])*256 + extended_arg
extended_arg = 0
i = i+2
if op == EXTENDED_ARG:
extended_arg = oparg*65536L
if op in hasconst:
yield co.co_consts[oparg]
elif op in hasname:
yield co.co_names[oparg]
elif op in hasjrel:
yield (i,oparg)
elif op in haslocal:
yield co.co_varnames[oparg]
elif op in hascompare:
yield cmp_op[oparg]
elif op in hasfree:
if free is None:
free = co.co_cellvars + co.co_freevars
yield free[oparg]

list(distrace(A.__init__.func_code))

['LOAD_GLOBAL', 'NotImplemented', 'RAISE_VARARGS', 'LOAD_CONST', None,
'RETURN_VALUE']
Ciao,
Kay

Jul 18 '05 #1
2 1287

"Kay Schluehr" <ka**********@gmx.net> wrote in message
news:11*********************@z14g2000cwz.googlegro ups.com...
class A(object):
def __init__(self):
raise NotImplemented

We can regard class A as a "pure abstract" class. It is impossible to
create instances of A.
You see that parameter 'self'? When __init__ is called, it is bound to an
already created instance of A. How soon the last reference goes away I am
not sure. If you change the raise statement to 'raise
SomeException(self)', then that instance will become bound to the 'args'
attribute of the resulting exception object. That exception object can in
turn be get captured by an 'except SomeException, obj' statement. The
binding of 'obj' persists after the except statement.

To abort object creation, write a metaclass with __new__ and raise an
exception there.
Thinking in Python makes live easier because we can not only
check interfaces superficially but we can inspect the code and we can
compare two code-objects on the behavioural level with a certain
accuracy.


You are actually thinking in terms of the CPython implementation, not
Python itself, which is fine as long as you don't expect CPython-specific
introspection to work with other implementations. But it is pretty neat
sometimes.

Terry J. Reedy

Jul 18 '05 #2
Terry Reedy wrote:
"Kay Schluehr" <ka**********@gmx.net> wrote in message
news:11*********************@z14g2000cwz.googlegro ups.com...
class A(object):
def __init__(self):
raise NotImplemented

We can regard class A as a "pure abstract" class. It is impossible to create instances of A.
You see that parameter 'self'? When __init__ is called, it is bound

to an already created instance of A.
Yes. I've forgotten.

This would make sense and instantiable subclasses have to override
__new__.

class A(object):
def __new__(cls):
raise NotImplemented

def __init__(self):
self.x = 0
How soon the last reference goes away I am
not sure. If you change the raise statement to 'raise
SomeException(self)', then that instance will become bound to the 'args' attribute of the resulting exception object. That exception object can in turn be get captured by an 'except SomeException, obj' statement. The binding of 'obj' persists after the except statement.
I considered the statement "raise Implemented" as kind of pattern: an
implementation that is at the same time a "declaration" for some client
who understands behavioural equivalence. One can not go much further
with the idea of scripting :)

On the other hand for real applications it may be wiser to use
decorators, because they are a standard and easy to use and to
understand.

This one would be completely appropriate:

def not_implemented(f):
setattr(f,"not_implemented",True)
return f

class A(object):
@not_implemented
def f(self):
pass
Allthough this one

class A(object):
def f(self):
raise NotImplemented

would be minimalistic and somehow innocent.
To abort object creation, write a metaclass with __new__ and raise an exception there.
Yes.
Thinking in Python makes live easier because we can not only
check interfaces superficially but we can inspect the code and we can compare two code-objects on the behavioural level with a certain
accuracy.
You are actually thinking in terms of the CPython implementation, not

Python itself, which is fine as long as you don't expect CPython-specific introspection to work with other implementations. But it is pretty neat sometimes.

Terry J. Reedy


I think it is scripting putting to the extreme. I don't know much about
the introspective capabilities of Jython and IronPython because I don't
use them but without them not even a variant of Pychecker will work for
either of the two Pychecker versions ( i.e. AST / bytecode ).

Ciao,
Kay

Jul 18 '05 #3

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

9
by: Jan van Veldhuizen | last post by:
I have an application which is running fine with MS SqlServer, but it should be working with Oracle as weel. At a lot of places we rely upon the ADO Recordset to return incremented identity...
112
by: Andy | last post by:
Hi All! We are doing new development for SQL Server 2000 and also moving from SQL 7.0 to SQL Server 2000. What are cons and pros for using IDENTITY property as PK in SQL SERVER 2000? Please,...
24
by: Ilija_G | last post by:
Hi, Is there any replace for "Select @@identity" that could return "just inserted" GUID as a primary key? Has anyone tested what's faster, working with Guid or Autonumber ?
0
by: Bhavna | last post by:
Hi im still hving a problem with my Identity value that is returned after my insert query What i have done is inserted a row into a datatable and then used an dataAdapters UPDATE command to...
99
by: Glen Herrmannsfeldt | last post by:
I was compiling a program written by someone else about six years ago, and widely distributed at the time. It also includes makefiles for many different systems, so I know it has been compiled...
3
by: Wm. Scott Miller | last post by:
What is the difference between using a username and password in the processmodel section vs using one in impersonation in the machine.config file? What are the advantages of each and what are the...
0
by: HarryHalpin | last post by:
IRW2006 - Identity, Reference, and the Web http://www.ibiblio.org/hhalpin/irw2006/ Co-located Workshop at WWW2006, Edinburgh Scotland, May First Call for Papers ===========
41
by: pb648174 | last post by:
In a multi-user environment, I would like to get a list of Ids generated, similar to: declare @LastId int select @LastId = Max(Id) From TableMania INSERT INTO TableMania (ColumnA, ColumnB)...
4
by: Saqib Ali | last post by:
I have some security concerns over storing a Active Directory username/ passwd in a text based web.config file for the identity impersonation definition. I know that web.conf is not accessible...
0
by: DolphinDB | last post by:
Tired of spending countless mintues downsampling your data? Look no further! In this article, you’ll learn how to efficiently downsample 6.48 billion high-frequency records to 61 million...
0
by: ryjfgjl | last post by:
ExcelToDatabase: batch import excel into database automatically...
1
isladogs
by: isladogs | last post by:
The next Access Europe meeting will be on Wednesday 6 Mar 2024 starting at 18:00 UK time (6PM UTC) and finishing at about 19:15 (7.15PM). In this month's session, we are pleased to welcome back...
0
by: Vimpel783 | last post by:
Hello! Guys, I found this code on the Internet, but I need to modify it a little. It works well, the problem is this: Data is sent from only one cell, in this case B5, but it is necessary that data...
0
by: ArrayDB | last post by:
The error message I've encountered is; ERROR:root:Error generating model response: exception: access violation writing 0x0000000000005140, which seems to be indicative of an access violation...
1
by: PapaRatzi | last post by:
Hello, I am teaching myself MS Access forms design and Visual Basic. I've created a table to capture a list of Top 30 singles and forms to capture new entries. The final step is a form (unbound)...
1
by: CloudSolutions | last post by:
Introduction: For many beginners and individual users, requiring a credit card and email registration may pose a barrier when starting to use cloud servers. However, some cloud server providers now...
0
by: af34tf | last post by:
Hi Guys, I have a domain whose name is BytesLimited.com, and I want to sell it. Does anyone know about platforms that allow me to list my domain in auction for free. Thank you
0
isladogs
by: isladogs | last post by:
The next Access Europe User Group meeting will be on Wednesday 3 Apr 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM). In this session, we are pleased to welcome former...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.