Skip to content
Projeler
Gruplar
Parçacıklar
Yardım
Yükleniyor...
Oturum aç / Kaydol
Gezinmeyi değiştir
C
cpython
Proje
Proje
Ayrıntılar
Etkinlik
Cycle Analytics
Depo (repository)
Depo (repository)
Dosyalar
Kayıtlar (commit)
Dallar (branch)
Etiketler
Katkıda bulunanlar
Grafik
Karşılaştır
Grafikler
Konular (issue)
0
Konular (issue)
0
Liste
Pano
Etiketler
Kilometre Taşları
Birleştirme (merge) Talepleri
0
Birleştirme (merge) Talepleri
0
CI / CD
CI / CD
İş akışları (pipeline)
İşler
Zamanlamalar
Grafikler
Paketler
Paketler
Wiki
Wiki
Parçacıklar
Parçacıklar
Üyeler
Üyeler
Collapse sidebar
Close sidebar
Etkinlik
Grafik
Grafikler
Yeni bir konu (issue) oluştur
İşler
Kayıtlar (commit)
Konu (issue) Panoları
Kenar çubuğunu aç
Batuhan Osman TASKAYA
cpython
Commits
0cdb8876
Kaydet (Commit)
0cdb8876
authored
Agu 26, 1997
tarafından
Guido van Rossum
Dosyalara gözat
Seçenekler
Dosyalara Gözat
İndir
Eposta Yamaları
Sade Fark
Completed first draft.
üst
91010551
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
467 additions
and
18 deletions
+467
-18
Eiffel.py
Demo/metaclasses/Eiffel.py
+1
-1
Simple.py
Demo/metaclasses/Simple.py
+45
-0
Synch.py
Demo/metaclasses/Synch.py
+256
-0
index.html
Demo/metaclasses/index.html
+165
-17
No files found.
Demo/metaclasses/Eiffel.py
Dosyayı görüntüle @
0cdb8876
...
...
@@ -107,7 +107,7 @@ def _test():
assert
Result
>
arg
x
=
C
()
x
.
m1
(
12
)
x
.
m1
(
-
1
)
##
x.m1(-1)
if
__name__
==
'__main__'
:
_test
()
Demo/metaclasses/Simple.py
0 → 100644
Dosyayı görüntüle @
0cdb8876
import
types
class
Tracing
:
def
__init__
(
self
,
name
,
bases
,
namespace
):
"""Create a new class."""
self
.
__name__
=
name
self
.
__bases__
=
bases
self
.
__namespace__
=
namespace
def
__call__
(
self
):
"""Create a new instance."""
return
Instance
(
self
)
class
Instance
:
def
__init__
(
self
,
klass
):
self
.
__klass__
=
klass
def
__getattr__
(
self
,
name
):
try
:
value
=
self
.
__klass__
.
__namespace__
[
name
]
except
KeyError
:
raise
AttributeError
,
name
if
type
(
value
)
is
not
types
.
FunctionType
:
return
value
return
BoundMethod
(
value
,
self
)
class
BoundMethod
:
def
__init__
(
self
,
function
,
instance
):
self
.
function
=
function
self
.
instance
=
instance
def
__call__
(
self
,
*
args
):
print
"calling"
,
self
.
function
,
"for"
,
self
.
instance
,
"with"
,
args
return
apply
(
self
.
function
,
(
self
.
instance
,)
+
args
)
Trace
=
Tracing
(
'Trace'
,
(),
{})
class
MyTracedClass
(
Trace
):
def
method1
(
self
,
a
):
self
.
a
=
a
def
method2
(
self
):
return
self
.
a
aninstance
=
MyTracedClass
()
aninstance
.
method1
(
10
)
print
aninstance
.
method2
()
Demo/metaclasses/Synch.py
0 → 100644
Dosyayı görüntüle @
0cdb8876
"""Synchronization metaclass.
This metaclass makes it possible to declare synchronized methods.
"""
import
thread
# First we need to define a reentrant lock.
# This is generally useful and should probably be in a standard Python
# library module. For now, we in-line it.
class
Lock
:
"""Reentrant lock.
This is a mutex-like object which can be acquired by the same
thread more than once. It keeps a reference count of the number
of times it has been acquired by the same thread. Each acquire()
call must be matched by a release() call and only the last
release() call actually releases the lock for acquisition by
another thread.
The implementation uses two locks internally:
__mutex is a short term lock used to protect the instance variables
__wait is the lock for which other threads wait
A thread intending to acquire both locks should acquire __wait
first.
The implementation uses two other instance variables, protected by
locking __mutex:
__tid is the thread ID of the thread that currently has the lock
__count is the number of times the current thread has acquired it
When the lock is released, __tid is None and __count is zero.
"""
def
__init__
(
self
):
"""Constructor. Initialize all instance variables."""
self
.
__mutex
=
thread
.
allocate_lock
()
self
.
__wait
=
thread
.
allocate_lock
()
self
.
__tid
=
None
self
.
__count
=
0
def
acquire
(
self
,
flag
=
1
):
"""Acquire the lock.
If the optional flag argument is false, returns immediately
when it cannot acquire the __wait lock without blocking (it
may still block for a little while in order to acquire the
__mutex lock).
The return value is only relevant when the flag argument is
false; it is 1 if the lock is acquired, 0 if not.
"""
self
.
__mutex
.
acquire
()
try
:
if
self
.
__tid
==
thread
.
get_ident
():
self
.
__count
=
self
.
__count
+
1
return
1
finally
:
self
.
__mutex
.
release
()
locked
=
self
.
__wait
.
acquire
(
flag
)
if
not
flag
and
not
locked
:
return
0
try
:
self
.
__mutex
.
acquire
()
assert
self
.
__tid
==
None
assert
self
.
__count
==
0
self
.
__tid
=
thread
.
get_ident
()
self
.
__count
=
1
return
1
finally
:
self
.
__mutex
.
release
()
def
release
(
self
):
"""Release the lock.
If this thread doesn't currently have the lock, an assertion
error is raised.
Only allow another thread to acquire the lock when the count
reaches zero after decrementing it.
"""
self
.
__mutex
.
acquire
()
try
:
assert
self
.
__tid
==
thread
.
get_ident
()
assert
self
.
__count
>
0
self
.
__count
=
self
.
__count
-
1
if
self
.
__count
==
0
:
self
.
__tid
=
None
self
.
__wait
.
release
()
finally
:
self
.
__mutex
.
release
()
def
_testLock
():
done
=
[]
def
f2
(
lock
,
done
=
done
):
lock
.
acquire
()
print
"f2 running in thread
%
d
\n
"
%
thread
.
get_ident
(),
lock
.
release
()
done
.
append
(
1
)
def
f1
(
lock
,
f2
=
f2
,
done
=
done
):
lock
.
acquire
()
print
"f1 running in thread
%
d
\n
"
%
thread
.
get_ident
(),
try
:
f2
(
lock
)
finally
:
lock
.
release
()
done
.
append
(
1
)
lock
=
Lock
()
lock
.
acquire
()
f1
(
lock
)
# Adds 2 to done
lock
.
release
()
lock
.
acquire
()
thread
.
start_new_thread
(
f1
,
(
lock
,))
# Adds 2
thread
.
start_new_thread
(
f1
,
(
lock
,
f1
))
# Adds 3
thread
.
start_new_thread
(
f2
,
(
lock
,))
# Adds 1
thread
.
start_new_thread
(
f2
,
(
lock
,))
# Adds 1
lock
.
release
()
import
time
while
len
(
done
)
<
9
:
print
len
(
done
)
time
.
sleep
(
0.001
)
print
len
(
done
)
# Now, the Locking metaclass is a piece of cake.
# As an example feature, methods whose name begins with exactly one
# underscore are not synchronized.
from
Meta
import
MetaClass
,
MetaHelper
,
MetaMethodWrapper
class
LockingMethodWrapper
(
MetaMethodWrapper
):
def
__call__
(
self
,
*
args
,
**
kw
):
if
self
.
__name__
[:
1
]
==
'_'
and
self
.
__name__
[
1
:]
!=
'_'
:
return
apply
(
self
.
func
,
(
self
.
inst
,)
+
args
,
kw
)
self
.
inst
.
__lock__
.
acquire
()
try
:
return
apply
(
self
.
func
,
(
self
.
inst
,)
+
args
,
kw
)
finally
:
self
.
inst
.
__lock__
.
release
()
class
LockingHelper
(
MetaHelper
):
__methodwrapper__
=
LockingMethodWrapper
def
__helperinit__
(
self
,
formalclass
):
MetaHelper
.
__helperinit__
(
self
,
formalclass
)
self
.
__lock__
=
Lock
()
class
LockingMetaClass
(
MetaClass
):
__helper__
=
LockingHelper
Locking
=
LockingMetaClass
(
'Locking'
,
(),
{})
def
_test
():
# For kicks, take away the Locking base class and see it die
class
Buffer
(
Locking
):
def
__init__
(
self
,
initialsize
):
assert
initialsize
>
0
self
.
size
=
initialsize
self
.
buffer
=
[
None
]
*
self
.
size
self
.
first
=
self
.
last
=
0
def
put
(
self
,
item
):
# Do we need to grow the buffer?
if
(
self
.
last
+
1
)
%
self
.
size
!=
self
.
first
:
# Insert the new item
self
.
buffer
[
self
.
last
]
=
item
self
.
last
=
(
self
.
last
+
1
)
%
self
.
size
return
# Double the buffer size
# First normalize it so that first==0 and last==size-1
print
"buffer ="
,
self
.
buffer
print
"first =
%
d, last =
%
d, size =
%
d"
%
(
self
.
first
,
self
.
last
,
self
.
size
)
if
self
.
first
<=
self
.
last
:
temp
=
self
.
buffer
[
self
.
first
:
self
.
last
]
else
:
temp
=
self
.
buffer
[
self
.
first
:]
+
self
.
buffer
[:
self
.
last
]
print
"temp ="
,
temp
self
.
buffer
=
temp
+
[
None
]
*
(
self
.
size
+
1
)
self
.
first
=
0
self
.
last
=
self
.
size
-
1
self
.
size
=
self
.
size
*
2
print
"Buffer size doubled to"
,
self
.
size
print
"new buffer ="
,
self
.
buffer
print
"first =
%
d, last =
%
d, size =
%
d"
%
(
self
.
first
,
self
.
last
,
self
.
size
)
self
.
put
(
item
)
# Recursive call to test the locking
def
get
(
self
):
# Is the buffer empty?
if
self
.
first
==
self
.
last
:
raise
EOFError
# Avoid defining a new exception
item
=
self
.
buffer
[
self
.
first
]
self
.
first
=
(
self
.
first
+
1
)
%
self
.
size
return
item
def
producer
(
buffer
,
wait
,
n
=
1000
):
import
time
i
=
0
while
i
<
n
:
print
"put"
,
i
buffer
.
put
(
i
)
i
=
i
+
1
print
"Producer: done producing"
,
n
,
"items"
wait
.
release
()
def
consumer
(
buffer
,
wait
,
n
=
1000
):
import
time
i
=
0
tout
=
0.001
while
i
<
n
:
try
:
x
=
buffer
.
get
()
if
x
!=
i
:
raise
AssertionError
,
\
"get() returned
%
s, expected
%
s"
%
(
x
,
i
)
print
"got"
,
i
i
=
i
+
1
tout
=
0.001
except
EOFError
:
time
.
sleep
(
tout
)
tout
=
tout
*
2
print
"Consumer: done consuming"
,
n
,
"items"
wait
.
release
()
pwait
=
thread
.
allocate_lock
()
pwait
.
acquire
()
cwait
=
thread
.
allocate_lock
()
cwait
.
acquire
()
buffer
=
Buffer
(
1
)
n
=
1000
thread
.
start_new_thread
(
consumer
,
(
buffer
,
cwait
,
n
))
thread
.
start_new_thread
(
producer
,
(
buffer
,
pwait
,
n
))
pwait
.
acquire
()
print
"Producer done"
cwait
.
acquire
()
print
"All done"
print
"buffer size =="
,
len
(
buffer
.
buffer
)
if
__name__
==
'__main__'
:
_testLock
()
_test
()
Demo/metaclasses/index.html
Dosyayı görüntüle @
0cdb8876
...
...
@@ -6,9 +6,9 @@
<BODY
BGCOLOR=
"FFFFFF"
>
<H1>
Metaprogramming in Python 1.5
</H1>
<H1>
Metaprogramming in Python 1.5
(DRAFT)
</H1>
<H4>
XXX
Don't link to this page! It
is very much a work in progress.
</H4>
<H4>
XXX
This
is very much a work in progress.
</H4>
<P>
While Python 1.5 is only out as a
<A
HREF=
"http://grail.cnri.reston.va.us/python/1.5a3/"
>
restricted alpha
...
...
@@ -267,7 +267,7 @@ class Instance:
value = self.__klass__.__namespace__[name]
except KeyError:
raise AttributeError, name
if type(value) is not types.FuncType:
if type(value) is not types.Func
tion
Type:
return value
return BoundMethod(value, self)
...
...
@@ -276,20 +276,150 @@ class BoundMethod:
self.function = function
self.instance = instance
def __call__(self, *args):
print "calling", self.function, "for", instance, "with", args
print "calling", self.function, "for",
self.
instance, "with", args
return apply(self.function, (self.instance,) + args)
<HR>
Confused already?
Trace = Tracing('Trace', (), {})
class MyTracedClass(Trace):
def method1(self, a):
self.a = a
def method2(self):
return self.a
aninstance = MyTracedClass()
aninstance.method1(10)
print "the answer is %d" % aninstance.method2()
</PRE>
Confused already? The intention is to read this from top down. The
Tracing class is the metaclass we're defining. Its structure is
really simple.
<P>
<UL>
<LI>
The __init__ method is invoked when a new Tracing instance is
created, e.g. the definition of class MyTracedClass later in the
example. It simply saves the class name, base classes and namespace
as instance variables.
<P>
<LI>
The __call__ method is invoked when a Tracing instance is called,
e.g. the creation of aninstance later in the example. It returns an
instance of the class Instance, which is defined next.
<P>
</UL>
<P>
The class Instance is the class used for all instances of classes
built using the Tracing metaclass, e.g. aninstance. It has two
methods:
<P>
<UL>
<LI>
The __init__ method is invoked from the Tracing.__call__ method
above to initialize a new instance. It saves the class reference as
an instance variable. It uses a funny name because the user's
instance variables (e.g. self.a later in the example) live in the same
namespace.
<P>
<LI>
The __getattr__ method is invoked whenever the user code
references an attribute of the instance that is not an instance
variable (nor a class variable; but except for __init__ and
__getattr__ there are no class variables). It will be called, for
example, when aninstance.method1 is referenced in the example, with
self set to aninstance and name set to the string "method1".
<P>
</UL>
<P>
The __getattr__ method looks the name up in the __namespace__
dictionary. If it isn't found, it raises an AttributeError exception.
(In a more realistic example, it would first have to look through the
base classes as well.) If it is found, there are two possibilities:
it's either a function or it isn't. If it's not a function, it is
assumed to be a class variable, and its value is returned. If it's a
function, we have to ``wrap'' it in instance of yet another helper
class, BoundMethod.
<P>
The BoundMethod class is needed to implement a familiar feature:
when a method is defined, it has an initial argument, self, which is
automatically bound to the relevant instance when it is called. For
example, aninstance.method1(10) is equivalent to method1(aninstance,
10). In the example if this call, first a temporary BoundMethod
instance is created with the following constructor call: temp =
BoundMethod(method1, aninstance); then this instance is called as
temp(10). After the call, the temporary instance is discarded.
<P>
<UL>
<
P>
XXX More text is needed here. For now, have a look at some very
preliminary examples that I coded up to teach myself how to use thi
s
feature:
<
LI>
The __init__ method is invoked for the constructor call
BoundMethod(method1, aninstance). It simply saves away it
s
arguments.
<P>
<LI>
The __call__ method is invoked when the bound method instance is
called, as in temp(10). It needs to call method1(aninstance, 10).
However, even though self.function is now method1 and self.instance is
aninstance, it can't call self.function(self.instance, args) directly,
because it should work regardless of the number of arguments passed.
(For simplicity, support for keyword arguments has been omitted.)
<P>
</UL>
<H2>
Real-life Examples
</H2>
<P>
In order to be able to support arbitrary argument lists, the
__call__ method first constructs a new argument tuple. Conveniently,
because of the notation *args in __call__'s own argument list, the
arguments to __call__ (except for self) are placed in the tuple args.
To construct the desired argument list, we concatenate a singleton
tuple containing the instance with the args tuple: (self.instance,) +
args. (Note the trailing comma used to construct the singleton
tuple.) In our example, the resulting argument tuple is (aninstance,
10).
<P>
The intrinsic function apply() takes a function and an argument
tuple and calls the function for it. In our example, we are calling
apply(method1, (aninstance, 10)) which is equivalent to calling
method(aninstance, 10).
<P>
From here on, things should come together quite easily. The output
of the example code is something like this:
<PRE>
calling
<function
method1
at
ae8d8
>
for
<Instance
instance
at
95ab0
>
with (10,)
calling
<function
method2
at
ae900
>
for
<Instance
instance
at
95ab0
>
with ()
the answer is 10
</PRE>
<P>
That was about the shortest meaningful example that I could come up
with. A real tracing metaclass (for example,
<A
HREF=
"#Trace"
>
Trace.py
</A>
discussed below) needs to be more
complicated in two dimensions.
<P>
First, it needs to support more advanced Python features such as
class variables, inheritance, __init__ methods, and keyword arguments.
<P>
Second, it needs to provide a more flexible way to handle the
actual tracing information; perhaps it should be possible to write
your own tracing function that gets called, perhaps it should be
possible to enable and disable tracing on a per-class or per-instance
basis, and perhaps a filter so that only interesting calls are traced;
it should also be able to trace the return value of the call (or the
exception it raised if an error occurs). Even the Trace.py example
doesn't support all these features yet.
<P>
<HR>
<H1>
Real-life Examples
</H1>
<P>
Have a look at some very preliminary examples that I coded up to
teach myself how to use metaprogramming:
<DL>
...
...
@@ -313,13 +443,13 @@ and ``Color.red + 1'' raise a TypeError exception.
<P>
<DT><A
HREF=
"Trace.py"
>
Trace.py
</A>
<DT><A
NAME=
Trace
></A><A
HREF=
"Trace.py"
>
Trace.py
</A>
<DD>
The resulting classes work much like standard
classes, but by
setting a special class or instance attribute __trace_output__ to
point to a file, all calls to the class's methods are traced. It wa
s
a
bit of a struggle to get this right. This should probably redone
using the generic metaclass below.
<DD>
The resulting classes work much like standard
classes, but by setting a special class or instance attribute
__trace_output__ to point to a file, all calls to the class's method
s
a
re traced. It was a bit of a struggle to get this right. This
should probably redone
using the generic metaclass below.
<P>
...
...
@@ -338,13 +468,31 @@ hooks tough; we provide a similar hook _getattr_ instead.
<P>
<DT><A
HREF=
"Eiffel.py"
>
Eiffel.py
</A>
ppp
<DD>
Uses the above generic metaclass to implement Eiffel style
pre-conditions and post-conditions.
<P>
<DT><A
HREF=
"Synch.py"
>
Synch.py
</A>
<DD>
Uses the above generic metaclass to implement synchronized
methods.
<P>
</DL>
<P>
A pattern seems to be emerging: almost all these uses of
metaclasses (except for Enum, which is probably more cute than useful)
mostly work by placing wrappers around method calls. An obvious
problem with that is that it's not easy to combine the features of
different metaclasses, while this would actually be quite useful: for
example, I wouldn't mind getting a trace from the test run of the
Synch module, and it would be interesting to add preconditions to it
as well. This needs more research. Perhaps a metaclass could be
provided that allows stackable wrappers...
</BODY>
</HTML>
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment