Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.lang.python > #83651 > unrolled thread
| Started by | Andrew Robinson <andrew3@r3dsolutions.com> |
|---|---|
| First post | 2015-01-12 17:59 -0800 |
| Last post | 2015-01-23 11:00 -0700 |
| Articles | 20 on this page of 48 — 11 participants |
Back to article view | Back to comp.lang.python
This discussion starts older than the indexed window; earlier articles aren't shown. The article labeled Started by
below is the oldest one visible, not the original post.
Re: Comparisons and sorting of a numeric class.... Andrew Robinson <andrew3@r3dsolutions.com> - 2015-01-12 17:59 -0800
Re: Comparisons and sorting of a numeric class.... Steven D'Aprano <steve@pearwood.info> - 2015-01-13 05:32 +0000
Re: Comparisons and sorting of a numeric class.... Chris Angelico <rosuav@gmail.com> - 2015-01-13 17:13 +1100
Re: Comparisons and sorting of a numeric class.... Terry Reedy <tjreedy@udel.edu> - 2015-01-13 05:49 -0500
Re: Comparisons and sorting of a numeric class.... Marko Rauhamaa <marko@pacujo.net> - 2015-01-13 13:00 +0200
Re: Comparisons and sorting of a numeric class.... Chris Angelico <rosuav@gmail.com> - 2015-01-13 22:20 +1100
Re: Comparisons and sorting of a numeric class.... Ian Kelly <ian.g.kelly@gmail.com> - 2015-01-13 11:56 -0700
Re: Comparisons and sorting of a numeric class.... Chris Angelico <rosuav@gmail.com> - 2015-01-14 06:02 +1100
Re: Comparisons and sorting of a numeric class.... Chris Angelico <rosuav@gmail.com> - 2015-01-13 21:56 +1100
Re: Comparisons and sorting of a numeric class.... Andrew Robinson <andrew3@r3dsolutions.com> - 2015-01-13 16:12 -0800
Re: Comparisons and sorting of a numeric class.... Ian Kelly <ian.g.kelly@gmail.com> - 2015-01-14 01:31 -0700
Re: Comparisons and sorting of a numeric class.... Ian Kelly <ian.g.kelly@gmail.com> - 2015-01-14 22:26 -0700
Re: Comparisons and sorting of a numeric class.... Gregory Ewing <greg.ewing@canterbury.ac.nz> - 2015-01-16 22:23 +1300
Re: Comparisons and sorting of a numeric class.... Andrew Robinson <andrew3@r3dsolutions.com> - 2015-01-14 23:23 -0800
Re: Comparisons and sorting of a numeric class.... Steven D'Aprano <steve@pearwood.info> - 2015-01-15 08:41 +0000
Re: Comparisons and sorting of a numeric class.... Andrew Robinson <andrew3@r3dsolutions.com> - 2015-01-15 17:45 -0800
Re: Comparisons and sorting of a numeric class.... Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2015-01-16 18:33 +1100
Re: Comparisons and sorting of a numeric class.... Gregory Ewing <greg.ewing@canterbury.ac.nz> - 2015-01-17 00:07 +1300
Re: Comparisons and sorting of a numeric class.... Rustom Mody <rustompmody@gmail.com> - 2015-01-16 03:22 -0800
Re: Comparisons and sorting of a numeric class.... Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2015-01-17 23:27 +1100
Re: Comparisons and sorting of a numeric class.... Mark Lawrence <breamoreboy@yahoo.co.uk> - 2015-01-16 02:27 +0000
Re: Comparisons and sorting of a numeric class.... Ian Kelly <ian.g.kelly@gmail.com> - 2015-01-15 22:01 -0700
Re: Comparisons and sorting of a numeric class.... Chris Angelico <rosuav@gmail.com> - 2015-01-16 17:58 +1100
Re: Comparisons and sorting of a numeric class.... Rob Gaddi <rgaddi@technologyhighland.invalid> - 2015-01-15 09:54 -0800
Re: Comparisons and sorting of a numeric class.... Andrew Robinson <andrew3@r3dsolutions.com> - 2015-01-26 15:47 -0800
Re: Comparisons and sorting of a numeric class.... Ian Kelly <ian.g.kelly@gmail.com> - 2015-01-26 18:50 -0700
Re: Comparisons and sorting of a numeric class.... Ian Kelly <ian.g.kelly@gmail.com> - 2015-01-15 10:05 -0700
Re: Comparisons and sorting of a numeric class.... Andrew Robinson <andrew3@r3dsolutions.com> - 2015-01-23 03:46 -0800
Re: Comparisons and sorting of a numeric class.... Rustom Mody <rustompmody@gmail.com> - 2015-01-23 07:31 -0800
Re: Comparisons and sorting of a numeric class.... Ian Kelly <ian.g.kelly@gmail.com> - 2015-01-23 09:50 -0700
Re: Comparisons and sorting of a numeric class.... Rustom Mody <rustompmody@gmail.com> - 2015-01-23 09:03 -0800
Re: Comparisons and sorting of a numeric class.... Chris Angelico <rosuav@gmail.com> - 2015-01-24 04:23 +1100
Re: Comparisons and sorting of a numeric class.... Rustom Mody <rustompmody@gmail.com> - 2015-01-23 09:29 -0800
Re: Comparisons and sorting of a numeric class.... Ian Kelly <ian.g.kelly@gmail.com> - 2015-01-23 11:04 -0700
Re: Comparisons and sorting of a numeric class.... Rustom Mody <rustompmody@gmail.com> - 2015-01-23 19:13 -0800
Re: Comparisons and sorting of a numeric class.... Rustom Mody <rustompmody@gmail.com> - 2015-01-23 19:21 -0800
Re: Comparisons and sorting of a numeric class.... Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2015-01-24 16:01 +1100
Re: Comparisons and sorting of a numeric class.... Chris Angelico <rosuav@gmail.com> - 2015-01-24 16:43 +1100
Re: Comparisons and sorting of a numeric class.... Rustom Mody <rustompmody@gmail.com> - 2015-01-23 09:22 -0800
Re: Comparisons and sorting of a numeric class.... Chris Angelico <rosuav@gmail.com> - 2015-01-24 04:37 +1100
Re: Comparisons and sorting of a numeric class.... Rustom Mody <rustompmody@gmail.com> - 2015-01-23 09:45 -0800
Re: Comparisons and sorting of a numeric class.... Chris Angelico <rosuav@gmail.com> - 2015-01-24 05:03 +1100
Re: Comparisons and sorting of a numeric class.... Ian Kelly <ian.g.kelly@gmail.com> - 2015-01-23 11:13 -0700
Re: Comparisons and sorting of a numeric class.... Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2015-01-24 19:27 +1100
Re: Comparisons and sorting of a numeric class.... Chris Angelico <rosuav@gmail.com> - 2015-01-23 22:58 +1100
Re: Comparisons and sorting of a numeric class.... Mark Lawrence <breamoreboy@yahoo.co.uk> - 2015-01-23 12:07 +0000
Re: Comparisons and sorting of a numeric class.... Terry Reedy <tjreedy@udel.edu> - 2015-01-23 11:17 -0500
Re: Comparisons and sorting of a numeric class.... Ian Kelly <ian.g.kelly@gmail.com> - 2015-01-23 11:00 -0700
Page 1 of 3 [1] 2 3 Next page →
| From | Andrew Robinson <andrew3@r3dsolutions.com> |
|---|---|
| Date | 2015-01-12 17:59 -0800 |
| Subject | Re: Comparisons and sorting of a numeric class.... |
| Message-ID | <mailman.17650.1421114374.18130.python-list@python.org> |
On 01/12/2015 02:35 PM, Chris Angelico wrote: > On Tue, Jan 13, 2015 at 9:27 AM, Andrew Robinson > <andrew3@r3dsolutions.com> wrote: >> Huh? I'm not adding any values when I merely subclass bool ; and even if the >> subclass could be instantiated -- that's doesn't mean a new value or >> instance of the base class (bool) must exist. For I could happily work with >> a new subclass that contains no new data, but only an already existing >> instance of 'True' or 'False' as its value source. That means there is no >> new value... but at most (and even that could be worked around) a new >> instance of a subclass containing an existing instance of it's base class. Hmmm.... That may be true in python, as it is now, but that doesn't mean that Guido had to leave it that way when he decided to change the language to single out bool, and make it's subclassing rules abnormal in the first place. He was changing the language when he made the decision after all !! What I am wanting to know is WHY did Guido think it so important to do that ? Why was he so focused on a strict inability to have any instances of a bool subclass at all -- that he made a very arbitrary exception to the general rule that base types in Python can be subclassed ? There's no reason in object oriented programming principles in general that requires a new subclass instance to be a COMPLETELY DISTINCT instance from an already existing superclass instance.... nor, have I ever seen Guido say that Python is designed intentionally to force this to always be the case... so I'm not sure that's its anything more than a non guaranteed implementation detail that Python acts the way you say it does.... I don't see, within Python, an intrinsic reason (other than lack of support/foresight in the historical evolution of Python to date), as to why a subclass couldn't be instanted with the data coming from an *already* existing instance of it's superclass. There is no need to copy data from an initialized superclass instance into a subclass instance that has no new data, but only rebind -- or add a binding/proxy object -- to bind the superclass instance to the subclass methods. eg: what is now standard practice to create a new copy of the superclass: class myFalse( bool ): __new__( self, data ): return super( myFalse, self ).__new__(self,data) Could be replaced by a general purpose proxy meant to handle singleton subclassing: class myFalse( bool ): __new__( self ): return bind_superinstance_to_subclass( False, myFalse ) > he Python bool type has the following > invariant, for any object x: > > assert not isinstance(x, bool) or x is True or x is False Really !??? Where, in the language definition, did Guido explicitly guarantee this invariant ? > (You can fiddle with this in Py2 by rebinding the names True and > False, but you could replace those names with (1==1) and (1==0) if you > want to be completely safe. Likewise, the name "bool" could be > replaced with (1==1).__class__ to avoid any stupidities there. But > conceptually, that's the invariant.) Interesting ... but rebinding True and False, won't extend the new capabilities to modules which are imported. They will still, I think, be bound to the old True and False values. I know, for example -- I can redefine the class bool altogether; although the type string becomes 'main.bool' -- none the less, it does not exist in default scope when I switch namespaces; eg: in a module being imported 'bool' still means the old version of class bool. I have to do something more drastic, like __builtins__.bool = class bool(int): ... And then, even modules will recognize the changed class definition. Hmmm..... >>> __builtins__.True='yes' >>> True 'yes' However, such actions -- I think -- are rather drastic; because they produce situations where another third party library in competition with mine might also have need of subclassing 'bool' and then we are in a fight for a static binding name with winner takes all ... rather than sharing dynamically compatible definitions based on subclasses. > Subclassing bool breaks this invariant, unless you never instantiate > the subclass, in which case it's completely useless. Well we can't subclass bool now -- and the language would have to change in order for us to be able to subclass it; So -- I don't think your assert statement guarantees anything if case the language changes... On the other hand, I also don't see that your assert statement would ever return False even as it is written --- even if the object (x) was sub-classed or a totally different object than True or False .... and so I *definitely* don't see why your assert statement would Fail if the language changed in one of several subtle ways I think it could. I mean, even right now -- with the language as-is -- let's define something that blatantly creates a new instance of something neither an actual instance of True nor False, and make that x -- and see if your assertion catches it: Python 2.7.5 (default, May 29 2013, 02:28:51) [GCC 4.8.0] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> x=(False,) >>> assert not isinstance(x, bool) or x is True or x is False >>> LOL ... no exception was raised... and we know if the assertion Failed, an exception ought to be raised: >>> assert False Traceback (most recent call last): File "<stdin>", line 1, in <module> AssertionError >>> And I'll prove my object is neither the instance True nor False itself, but a tupple wrap of it: >>> (False,)==False False >>> So -- your assertion, at least as shown, is pretty useless in helping determine why subclassing is not allowed, or instances of subclasses that are not distinct from their superclasses existing instance.
[toc] | [next] | [standalone]
| From | Steven D'Aprano <steve@pearwood.info> |
|---|---|
| Date | 2015-01-13 05:32 +0000 |
| Message-ID | <54b4aded$0$2738$c3e8da3$76491128@news.astraweb.com> |
| In reply to | #83651 |
On Mon, 12 Jan 2015 17:59:42 -0800, Andrew Robinson wrote:
[...]
> What I am wanting to know is WHY did Guido think it so important to do
> that ? Why was he so focused on a strict inability to have any
> instances of a bool subclass at all -- that he made a very arbitrary
> exception to the general rule that base types in Python can be
> subclassed ?
It's not arbitrary. All the singleton (doubleton in the case of bool)
classes cannot be subclassed. E.g. NoneType:
py> class X(type(None)):
... pass
...
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: Error when calling the metaclass bases
type 'NoneType' is not an acceptable base type
Likewise for the NotImplemented and Ellipsis types.
The reason is the same: if a type promises that there is one and only one
instance (two in the case of bool), then allowing subtypes will break
that promise in the 99.99% of cases where the subtype is instantiated.
I suppose in principle Python could allow you to subclass singleton
classes to your hearts content, and only raise an error if you try to
instantiate them, but that would probably be harder and more error-prone
to implement, and would *definitely* be harder to explain.
There may be others too:
py> from types import FunctionType
py> class F(FunctionType):
... pass
...
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: Error when calling the metaclass bases
type 'function' is not an acceptable base type
My guess here is that functions are so tightly coupled to the Python
interpreter that allowing you to subclass them, and hence break required
invariants, could crash the interpreter. Crashing the interpreter from
pure Python code is *absolutely not allowed*, so anything which would
allow that is forbidden.
> There's no reason in object oriented programming principles in general
> that requires a new subclass instance to be a COMPLETELY DISTINCT
> instance from an already existing superclass instance....
True. But what is the point of such a subclass? I don't think you have
really thought this through in detail.
Suppose we allowed bool subclasses, and we implement one which *only*
returns True and False, without adding a third instance:
class MyBool(bool):
def __new__(cls, arg):
if cls.condition(arg):
return True
else:
return False
@classmethod
def condition(cls, obj):
# decide whether obj is true-ish or false-ish.
pass
def spam(self):
return self.eggs()
def eggs(self):
return 23
And then you do this:
flag = MyBool(something)
flag.spam()
What do you expect to happen?
Since flag can *only* be a regular bool, True or False, it won't have
spam or eggs methods.
You might think of writing the code using unbound methods:
MyBool.spam(flag)
(assuming that methods don't enforce the type restriction that "self"
must be an instance of their class), but that fails when the spam method
calls "self.eggs". So you have to write your methods like this:
def spam(self):
return MyBool.eggs(self)
hard-coding the class name! You can't use type(self), because that's
regular bool, not MyBool.
This is a horrible, error-prone, confusing mess of a system. If you're
going to write code like this, you are better off making MyBool a module
with functions instead of a class with methods.
> nor, have I
> ever seen Guido say that Python is designed intentionally to force this
> to always be the case... so I'm not sure that's its anything more than a
> non guaranteed implementation detail that Python acts the way you say it
> does....
It is a documented restriction on bool. Whether you agree with the
decision or not, it is not an implementation detail, it is a language
promise.
https://docs.python.org/2/library/functions.html#bool
--
Steven
[toc] | [prev] | [next] | [standalone]
| From | Chris Angelico <rosuav@gmail.com> |
|---|---|
| Date | 2015-01-13 17:13 +1100 |
| Message-ID | <mailman.17659.1421129635.18130.python-list@python.org> |
| In reply to | #83661 |
On Tue, Jan 13, 2015 at 4:32 PM, Steven D'Aprano <steve@pearwood.info> wrote: > Crashing the interpreter from > pure Python code is *absolutely not allowed*, so anything which would > allow that is forbidden. Except when you willingly shoot yourself in the foot. rosuav@sikorsky:~$ python Python 2.7.3 (default, Mar 13 2014, 11:03:55) [GCC 4.7.2] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> import sys >>> def f(): sys.setrecursionlimit(sys.getrecursionlimit()+1) or f() ... >>> f() Segmentation fault rosuav@sikorsky:~$ python3 Python 3.5.0a0 (default:1c51f1650c42+, Dec 29 2014, 02:29:06) [GCC 4.7.2] on linux Type "help", "copyright", "credits" or "license" for more information. >>> import sys >>> def f(): sys.setrecursionlimit(sys.getrecursionlimit()+1) or f() ... >>> f() Segmentation fault But otherwise, yes. You shouldn't be able to segfault Python with Python code. ChrisA
[toc] | [prev] | [next] | [standalone]
| From | Terry Reedy <tjreedy@udel.edu> |
|---|---|
| Date | 2015-01-13 05:49 -0500 |
| Message-ID | <mailman.17669.1421146184.18130.python-list@python.org> |
| In reply to | #83661 |
On 1/13/2015 1:13 AM, Chris Angelico wrote: > On Tue, Jan 13, 2015 at 4:32 PM, Steven D'Aprano <steve@pearwood.info> wrote: >> Crashing the interpreter from >> pure Python code is *absolutely not allowed*, so anything which would >> allow that is forbidden. > > Except when you willingly shoot yourself in the foot. > > rosuav@sikorsky:~$ python > Python 2.7.3 (default, Mar 13 2014, 11:03:55) > [GCC 4.7.2] on linux2 > Type "help", "copyright", "credits" or "license" for more information. >>>> import sys >>>> def f(): sys.setrecursionlimit(sys.getrecursionlimit()+1) or f() > ... >>>> f() > Segmentation fault > rosuav@sikorsky:~$ python3 > Python 3.5.0a0 (default:1c51f1650c42+, Dec 29 2014, 02:29:06) > [GCC 4.7.2] on linux > Type "help", "copyright", "credits" or "license" for more information. >>>> import sys >>>> def f(): sys.setrecursionlimit(sys.getrecursionlimit()+1) or f() > ... >>>> f() > Segmentation fault > > But otherwise, yes. You shouldn't be able to segfault Python with Python code. I would have expected an out-of-memory error. If there is not already a crash issue on the tracker for this, you could add one. -- Terry Jan Reedy
[toc] | [prev] | [next] | [standalone]
| From | Marko Rauhamaa <marko@pacujo.net> |
|---|---|
| Date | 2015-01-13 13:00 +0200 |
| Message-ID | <87lhl7c5dt.fsf@elektro.pacujo.net> |
| In reply to | #83681 |
Terry Reedy <tjreedy@udel.edu>: > On 1/13/2015 1:13 AM, Chris Angelico wrote: >>>>> def f(): sys.setrecursionlimit(sys.getrecursionlimit()+1) or f() >> ... >>>>> f() >> Segmentation fault >> >> But otherwise, yes. You shouldn't be able to segfault Python with >> Python code. > > I would have expected an out-of-memory error. If there is not already > a crash issue on the tracker for this, you could add one. Linux grants memory it doesn't have. The truth comes out as a segmentation fault. Call it modern banking. The code above, though, shouldn't consume memory since it is a simple tail-recursive loop. Marko
[toc] | [prev] | [next] | [standalone]
| From | Chris Angelico <rosuav@gmail.com> |
|---|---|
| Date | 2015-01-13 22:20 +1100 |
| Message-ID | <mailman.17671.1421148016.18130.python-list@python.org> |
| In reply to | #83683 |
On Tue, Jan 13, 2015 at 10:00 PM, Marko Rauhamaa <marko@pacujo.net> wrote: > Terry Reedy <tjreedy@udel.edu>: > >> On 1/13/2015 1:13 AM, Chris Angelico wrote: >>>>>> def f(): sys.setrecursionlimit(sys.getrecursionlimit()+1) or f() >>> ... >>>>>> f() >>> Segmentation fault >>> >>> But otherwise, yes. You shouldn't be able to segfault Python with >>> Python code. >> >> I would have expected an out-of-memory error. If there is not already >> a crash issue on the tracker for this, you could add one. > > Linux grants memory it doesn't have. The truth comes out as a > segmentation fault. Call it modern banking. I'm not sure it's a Linux problem. The same thing happens on Windows, only without the "Segmentation fault" line. The exact behaviour on stack overflow would depend on the layout of memory... back when I wrote real-mode single-segment programs in DOS, the standard layout was PSP, then code, then data, then stack, so overusing the stack would overwrite your static data (and then, if you're really bad, your code as well... things got fun then). My best guess now would be that there's an actual boundary to the stack, and trying to access beyond that triggers an immediate error - "you're not allowed to use that memory". Nothing to do with over-allocating memory. > The code above, though, shouldn't consume memory since it is a simple > tail-recursive loop. Only if the interpreter can optimize it away. Bear in mind that it doesn't _return_ the result of that expression, so it needs to take whatever f() returns, discard it, and return None. But I could have defeated that, if I'd wanted to, by coding it to reduce the recursion limit again after the call - it can't optimize away a non-tail-call. Point is, if you take away the recursion limit, you _can_ crash CPython. Likewise if you fiddle around with ctypes: $ python3 Python 3.5.0a0 (default:1c51f1650c42+, Dec 29 2014, 02:29:06) [GCC 4.7.2] on linux Type "help", "copyright", "credits" or "license" for more information. >>> import ctypes >>> ctypes.cast(id(1), ctypes.POINTER(ctypes.c_int))[6]=2 >>> 1 Segmentation fault Not a bug. just a gun that you can shoot yourself in the foot with. (Code nicked from http://www.reddit.com/r/Python/comments/2441cv/can_you_change_the_value_of_1/ - from a comment saying "changing 1 this way just destroys everything". Yep, it does, and pretty quckly too.) ChrisA
[toc] | [prev] | [next] | [standalone]
| From | Ian Kelly <ian.g.kelly@gmail.com> |
|---|---|
| Date | 2015-01-13 11:56 -0700 |
| Message-ID | <mailman.17691.1421175432.18130.python-list@python.org> |
| In reply to | #83683 |
On Tue, Jan 13, 2015 at 4:20 AM, Chris Angelico <rosuav@gmail.com> wrote: > On Tue, Jan 13, 2015 at 10:00 PM, Marko Rauhamaa <marko@pacujo.net> wrote: >> The code above, though, shouldn't consume memory since it is a simple >> tail-recursive loop. > > Only if the interpreter can optimize it away. Bear in mind that it > doesn't _return_ the result of that expression, so it needs to take > whatever f() returns, discard it, and return None. And CPython doesn't even try to optimize tail recursion anyway.
[toc] | [prev] | [next] | [standalone]
| From | Chris Angelico <rosuav@gmail.com> |
|---|---|
| Date | 2015-01-14 06:02 +1100 |
| Message-ID | <mailman.17692.1421175743.18130.python-list@python.org> |
| In reply to | #83683 |
On Wed, Jan 14, 2015 at 5:56 AM, Ian Kelly <ian.g.kelly@gmail.com> wrote: > On Tue, Jan 13, 2015 at 4:20 AM, Chris Angelico <rosuav@gmail.com> wrote: >> On Tue, Jan 13, 2015 at 10:00 PM, Marko Rauhamaa <marko@pacujo.net> wrote: >>> The code above, though, shouldn't consume memory since it is a simple >>> tail-recursive loop. >> >> Only if the interpreter can optimize it away. Bear in mind that it >> doesn't _return_ the result of that expression, so it needs to take >> whatever f() returns, discard it, and return None. > > And CPython doesn't even try to optimize tail recursion anyway. I presumed that Marko's "shouldn't" was "CPython should be smarter than this". ChrisA
[toc] | [prev] | [next] | [standalone]
| From | Chris Angelico <rosuav@gmail.com> |
|---|---|
| Date | 2015-01-13 21:56 +1100 |
| Message-ID | <mailman.17670.1421146616.18130.python-list@python.org> |
| In reply to | #83661 |
On Tue, Jan 13, 2015 at 9:49 PM, Terry Reedy <tjreedy@udel.edu> wrote: > I would have expected an out-of-memory error. If there is not already a > crash issue on the tracker for this, you could add one. It's a stack fault, and it's a documented possibility: https://docs.python.org/2/library/sys.html#sys.setrecursionlimit https://docs.python.org/3/library/sys.html#sys.setrecursionlimit Tracker search brings up this as a definite hit, and some other maybes: http://bugs.python.org/issue6356 ChrisA
[toc] | [prev] | [next] | [standalone]
| From | Andrew Robinson <andrew3@r3dsolutions.com> |
|---|---|
| Date | 2015-01-13 16:12 -0800 |
| Message-ID | <mailman.17698.1421194465.18130.python-list@python.org> |
| In reply to | #83661 |
On 01/12/2015 09:32 PM, Steven D'Aprano wrote:
> On Mon, 12 Jan 2015 17:59:42 -0800, Andrew Robinson wrote:
>
> [...]
>> What I am wanting to know is WHY did Guido think it so important to do
>> that ? Why was he so focused on a strict inability to have any
>> instances of a bool subclass at all -- that he made a very arbitrary
>> exception to the general rule that base types in Python can be
>> subclassed ?
> It's not arbitrary. All the singleton (doubleton in the case of bool)
> classes cannot be subclassed. E.g. NoneType:
>
> py> class X(type(None)):
> ... pass
> ...
> Traceback (most recent call last):
> File "<stdin>", line 1, in <module>
> TypeError: Error when calling the metaclass bases
> type 'NoneType' is not an acceptable base type
>
>
> Likewise for the NotImplemented and Ellipsis types.
>
> The reason is the same: if a type promises that there is one and only one
> instance (two in the case of bool), then allowing subtypes will break
> that promise in the 99.99% of cases where the subtype is instantiated.
Ok. That's something I did not know. So much for the just four classes
can't be subtyped remark someone else made...
At least Guido is consistent.
But that doesn't give me any idea of why he thought it important.
> I suppose in principle Python could allow you to subclass singleton
> classes to your hearts content, and only raise an error if you try to
> instantiate them, but that would probably be harder and more error-prone
> to implement, and would *definitely* be harder to explain.
>
>
> There may be others too:
>
> py> from types import FunctionType
> py> class F(FunctionType):
> ... pass
> ...
> Traceback (most recent call last):
> File "<stdin>", line 1, in <module>
> TypeError: Error when calling the metaclass bases
> type 'function' is not an acceptable base type
>
>
> My guess here is that functions are so tightly coupled to the Python
> interpreter that allowing you to subclass them, and hence break required
> invariants, could crash the interpreter. Crashing the interpreter from
> pure Python code is *absolutely not allowed*, so anything which would
> allow that is forbidden.
>
>
>> There's no reason in object oriented programming principles in general
>> that requires a new subclass instance to be a COMPLETELY DISTINCT
>> instance from an already existing superclass instance....
> True. But what is the point of such a subclass? I don't think you have
> really thought this through in detail.
I have. Such a subclass allows refining of the meaning/precision of a
previous type while improving compatibility with existing applications
-- and without being so easy to do that everyone will abuse it. That's
the standard kind of things which go into deciding that a singleton is
appropriate...
Subclasses of singletons is a tried and true way of improving
productivity regardless of how it is implemented.
> Suppose we allowed bool subclasses, and we implement one which *only*
> returns True and False, without adding a third instance:
>
> class MyBool(bool):
> def __new__(cls, arg):
> if cls.condition(arg):
> return True
> else:
> return False
> @classmethod
> def condition(cls, obj):
> # decide whether obj is true-ish or false-ish.
> pass
> def spam(self):
> return self.eggs()
> def eggs(self):
> return 23
>
>
> And then you do this:
>
> flag = MyBool(something)
> flag.spam()
>
>
> What do you expect to happen?
Flag is not an instance of MyBool, so it's going to generate an exception.
>
> Since flag can *only* be a regular bool, True or False, it won't have
> spam or eggs methods.
Correct. It would generate an exception.
> You might think of writing the code using unbound methods:
>
> MyBool.spam(flag)
>
> (assuming that methods don't enforce the type restriction that "self"
> must be an instance of their class), but that fails when the spam method
> calls "self.eggs". So you have to write your methods like this:
>
> def spam(self):
> return MyBool.eggs(self)
>
> hard-coding the class name! You can't use type(self), because that's
> regular bool, not MyBool.
>
> This is a horrible, error-prone, confusing mess of a system. If you're
> going to write code like this, you are better off making MyBool a module
> with functions instead of a class with methods.
Every design method has its trade offs... but how you organize you code
will affect whether it is messy or clean.
In terms of information encoding -- both an instance of a type, or a
class definition held in a type variable -- eg: a class name -- are
pretty much interchangeable when it comes to being able to tell two
items are not the same one, or are the same one.
So -- even a cursory thought shows that the information could be encoded
in a very few lines even without an instance of a subclass:
class CAllFalse():
@classmethod
def __nonzero__(Kls): return False
class CPartFalse():
@classmethod
def __nonzero__(Kls): return False
...
class cmp():
lex=( CAllFalse, CPartFalse, True )
@staticmethod
def __cmp__( left, right ):
if type(left) == type(right): # advanced statistical compare
return cmp.lex.index( right ).__cmp__( cmp.lex.index( left ) )
else: # legacy bool compare...
return right.__nonzero__().__cmp__( left.__nonzero__() )
>>> CAllFalse.__nonzero__()
False
>>> cmp.__cmp__( CPartFalse, CAllFalse )
-1
>>> cmp.__cmp__( CPartFalse, False )
0
This is not optimal code, but I could definitely refine it until it was
compact and clean.
I do admit that, being more used to instantated variables, I can make
proxy wrappers far more easily and robustly than subclassed ones with no
instances at all. Both methods are traditionally used for making the
equivalent of a subclass of a singleton...
And it's pretty clear that the return values of comparison operators are
what have to be preserved in legacy operations vs. advanced ones
regardless of how the singletons are implemented.
Essentially, any type of if statement that mixes bool with an advanced
class must be wanting a legacy result -- so basically, I just need a
simple way to encode and carry out a complicated compare, and always
return a True or False final value at the appropriate time...
The most basic wrapper class is merely one that extends the idea of
comparision to rich comparision; eg: like this one...
def _op(op,other): # Call an operation, to match data sizes...
try: other[0]
except: return op((other,))
return op(other)
class ETuple(tuple):
"""
Enhanced Tuple.
A tuple which does rich comparisons even when the item being
compared against is not an iterable by converting non iterables to
tuples of length, exactly one.
"""
def __new__(cls, data):return super(ETuple,cls).__new__(cls,data)
def __eq__(self,other):return _op(super(ETuple,self).__eq__,other)
def __ne__(self,other):return _op(super(ETuple,self).__ne__,other)
def __lt__(self,other):return _op(super(ETuple,self).__lt__,other)
def __le__(self,other):return _op(super(ETuple,self).__le__,other)
def __gt__(self,other):return _op(super(ETuple,self).__gt__,other)
def __ge__(self,other):return _op(super(ETuple,self).__ge__,other)
def __cmp__(self,other):return _op(super(ETuple,self).__cmp__,other)
def __nonzero__(self,other):return
_op(super(ETuple,self).__cmp__,other)
>>> PartFalse = ETuple( (False,) )
>>> True > PartFalse
True
>>> PartFalse < True
True
>>> PartFalse > False
False
>>> False < PartFalse
False
>>> False == PartFalse
True
>>> False is PartFalse
False
>>> False == True
False
>>> print PartFalse
(False,)
It's pretty well behaved, and does everything I want.
A full implementation, that distinguishes between legacy and
probabilistic interpretations for the math operators could be done in
several ways; Here's one that I think is reasonably clean.
class RBool(tuple):
"""
Rich compare bool.
A tuple which contains a bool, and a relative certainty value.
Whenever a RBool is compared against a bool or single valued object
tuple,
it will default to legacy mode and compare only against the bool
However, whenever compared against another RBool, or multi-element
tuple,
it will do a full rich comparison.
"""
def __new__(cls, data):return super(RBool,cls).__new__(cls,data)
def _legacy(self,other):
'Resize local data for proper legacy compares as needed.'
try:
if len(other)==1: return (self[0],) # legacy compare in
tuples.
except TypeError: return self[0] # non iterables are a legacy
compare.
return tuple(self) # A full rich non-recursive compare is
warranted
def __eq__(self,other):return self._legacy(other) == other
def __ne__(self,other):return self._legacy(other) != other
def __lt__(self,other):return self._legacy(other) < other
def __le__(self,other):return self._legacy(other) <= other
def __gt__(self,other):return self._legacy(other) > other
def __ge__(self,other):return self._legacy(other) >= other
# def __cmp__( #TODO:
# def __nonzero__( #TODO:
AllTrue = True
PartTrue = RBool((False,3))
Uncertain = RBool((False,2))
PartFalse = RBool((False,1))
AllFalse = RBool((False,0))
>> nor, have I
>> ever seen Guido say that Python is designed intentionally to force this
>> to always be the case... so I'm not sure that's its anything more than a
>> non guaranteed implementation detail that Python acts the way you say it
>> does....
> It is a documented restriction on bool. Whether you agree with the
> decision or not, it is not an implementation detail, it is a language
> promise.
>
> https://docs.python.org/2/library/functions.html#bool
It was documented in the link I gave too. So -- It's not like I didn't
know that already. It just doesn't look like he had thought it out
carefully and might change his mind in the future. He is great on the
saying what will happen -- but not so good at clearly stating why, and
he does change his mind. I do recall reading about a time when Python
had a bool factory function ... with a plethora of values...
And even if he doesn't change his mind -- it's still a language
implementation detail; because Proxy objects are still possible even if
subclassing is not. And, it is therefore possible to get an object
which is not bool -- to return isinstance( myvar, bool ) True -- when it
proxies a bool (I have actually done this...)
Besides which, If you really want to get down to it -- I can redefine
class bool from main and make it subclassable for selected libraries I
want to import; it's hairy, but I certainly can make a replacement class
that will allow subclassing and which is compatible with the present
definition of bool.
So -- it's not like Guido's choices (including saying that duck-typing
ruins the whole point of having bools) -- are carved in stone for all
eternity.
There are always options....
[toc] | [prev] | [next] | [standalone]
| From | Ian Kelly <ian.g.kelly@gmail.com> |
|---|---|
| Date | 2015-01-14 01:31 -0700 |
| Message-ID | <mailman.17706.1421224325.18130.python-list@python.org> |
| In reply to | #83661 |
On Tue, Jan 13, 2015 at 5:12 PM, Andrew Robinson
<andrew3@r3dsolutions.com> wrote:
> So -- even a cursory thought shows that the information could be encoded in
> a very few lines even without an instance of a subclass:
>
> class CAllFalse():
> @classmethod
> def __nonzero__(Kls): return False
>
> class CPartFalse():
> @classmethod
> def __nonzero__(Kls): return False
This doesn't actually work.
>>> class CAllFalse(object):
... @classmethod
... def __nonzero__(cls): return False
...
>>> bool(CAllFalse)
True
When Python evaluates the "truthiness" of an object, it looks up the
__nonzero__ method on the object's class, not on the object itself
(same goes for all the other special dunder methods). Since CAllFalse
is itself a class, its own class (or rather, metaclass) is type [1].
Thus there's no point in using @classmethod on one of these special
methods. If you really want to proceed with this, you would have to
override the metaclass and define its __nonzero__ to return False,
like so:
>>> class CAllFalseMeta(type):
... def __nonzero__(self): return False
...
>>> class CAllFalse(object):
... __metaclass__ = CAllFalseMeta
...
>>> bool(CAllFalse)
False
In this code CAllFalse is an instance of CAllFalseMeta, while also
being a class itself. But I'm not sure what the point of this is,
when you could just make CAllFalse a normal class and create an
instance of it.
[1] Actually, its metaclass is only type if you inherit from object,
which you didn't do, although in Python 2.x it must be done
explicitly. If you don't inherit from object, you get what is known as
a "classic class" instead of a new-style class. There is no reason to
use the old-style classes unless you really know what you are doing.
> class cmp():
> lex=( CAllFalse, CPartFalse, True )
> @staticmethod
> def __cmp__( left, right ):
> if type(left) == type(right): # advanced statistical compare
> return cmp.lex.index( right ).__cmp__( cmp.lex.index( left ) )
> else: # legacy bool compare...
> return right.__nonzero__().__cmp__( left.__nonzero__() )
Magic methods are generally meant to be used by the interpreter, not
called directly. The Pythonic way to write that last line would have
been:
return cmp(bool(right), bool(left))
(Assuming of course that you rename the class to something that
doesn't shadow the cmp builtin function). This also would have
revealed sooner that your __nonzero__ method isn't actually invoked by
the interpreter.
> A full implementation, that distinguishes between legacy and probabilistic
> interpretations for the math operators could be done in several ways;
> Here's one that I think is reasonably clean.
>
> class RBool(tuple):
> """
> Rich compare bool.
> A tuple which contains a bool, and a relative certainty value.
> Whenever a RBool is compared against a bool or single valued object
> tuple,
> it will default to legacy mode and compare only against the bool
> However, whenever compared against another RBool, or multi-element
> tuple,
> it will do a full rich comparison.
> """
> def __new__(cls, data):return super(RBool,cls).__new__(cls,data)
> def _legacy(self,other):
> 'Resize local data for proper legacy compares as needed.'
> try:
> if len(other)==1: return (self[0],) # legacy compare in tuples.
> except TypeError: return self[0] # non iterables are a legacy
> compare.
> return tuple(self) # A full rich non-recursive compare is warranted
> def __eq__(self,other):return self._legacy(other) == other
> def __ne__(self,other):return self._legacy(other) != other
> def __lt__(self,other):return self._legacy(other) < other
> def __le__(self,other):return self._legacy(other) <= other
> def __gt__(self,other):return self._legacy(other) > other
> def __ge__(self,other):return self._legacy(other) >= other
> # def __cmp__( #TODO:
> # def __nonzero__( #TODO:
>
> AllTrue = True
> PartTrue = RBool((False,3))
> Uncertain = RBool((False,2))
> PartFalse = RBool((False,1))
> AllFalse = RBool((False,0))
This seems fine, but I don't get why you're messing around with
subclassing tuples. Do you really want to have boolean values that are
iterable, indexable, etc. for no reason? Just create a normal class
(or maybe subclass int, to match the normal bool class) and let your
instances contain a tuple instead of being one.
[toc] | [prev] | [next] | [standalone]
| From | Ian Kelly <ian.g.kelly@gmail.com> |
|---|---|
| Date | 2015-01-14 22:26 -0700 |
| Message-ID | <mailman.17739.1421299637.18130.python-list@python.org> |
| In reply to | #83661 |
On Wed, Jan 14, 2015 at 8:00 PM, Andrew Robinson
<andrew3@r3dsolutions.com> wrote:
> Hi Ian,
> On 01/14/2015 12:31 AM, Ian Kelly wrote:
>
> On Tue, Jan 13, 2015 at 5:12 PM, Andrew Robinson
> <andrew3@r3dsolutions.com> wrote:
>
> So -- even a cursory thought shows that the information could be encoded in
> a very few lines even without an instance of a subclass:
>
> class CAllFalse():
> @classmethod
> def __nonzero__(Kls): return False
>
> class CPartFalse():
> @classmethod
> def __nonzero__(Kls): return False
>
> This doesn't actually work.
>
> class CAllFalse(object):
>
> ... @classmethod
> ... def __nonzero__(cls): return False
> ...
>
> bool(CAllFalse)
>
> True
>
> Yes, but I never used it that way.
> If I had not put the classmethod on there, I could not have used it with a
> class name in lieu of an instance...
>
> eg:
> class Dummy():
> def __nonzero__(self): return False
>
> bool( Dummy.__nonzero__() )
>>>> bool( Dummy.__nonzero__() )
> Traceback (most recent call last):
> File "<stdin>", line 1, in <module>
> TypeError: unbound method __nonzero__() must be called with Dummy instance
> as first argument (got nothing instead)
>
> When Python evaluates the "truthiness" of an object, it looks up the
> __nonzero__ method on the object's class, not on the object itself
>
> Unless I call the __nonzero__ method using the underscores as I did...
So are you always going to call __nonzero__ then? When you have an
uncertain boolean that you want to use in an if expression, are you
going to write:
if value.__nonzero__():
every single time? Or is it possible that client code that isn't aware
of the extended bool might occasionally just use:
if value:
and thus interpret CAllFalse as True? The entire point of the
__nonzero__ method is to specify for the Python *interpreter* whether
it should treat the object as truish or falsish. If you're not
interested in overriding that, then __nonzero__ is the wrong name for
the method. Dunder names are reserved for use by Python, and when you
use one, it signals that this method has a special meaning to the
interpreter. If the interpreter doesn't actually use that, then you're
just sending a confusing signal to readers of your code. Call your
method nonzero() or bool() or is_true() or whatever you want, but
don't call it __nonzero__ because that's not what it is.
> Here's a clue:
>
> It may be easier to understand my example if you take something into
> account: D'Aprano at the end of his email reply quoted to me a page from
> the Python online manuals and called it a "Promise", rather than telling me
> why Guido made a choice in denying subclassing of bool in the first
> place.... (which is what I asked about...)
It seems to me the answer to that has been given to you several times
already in this thread, but you don't seem to want to accept it for
some reason. You can't subclass bool because instances of the subclass
would be new instances of bool, which would break the class invariant.
Without instances all you're left with are static methods, at which
point there is no particular reason to host them on a subclass.
> So -- If you will now notice, I
> specifically went out of my way to use __cmp__ and __nonzero__ methods in
> my limited example and they are both methods which have been discontinued in
> Python 3 in favor of more boolean friendly versions...
That would seem like all the less reason to use them, if you have any
interest in Python 3 compatibility.
> Given that Van Rosen
You mean Van Rossum?
> said that he denied subclassing of bool to prevent
> instances of bool, (eg: as a means to an end, not the end itself...) I can
> see working up an example with no instances as a proposal that might make
> Guido happy and allow me to work around Guido's objections; but on the other
> hand -- I don't see putting a lot of effort into it until all other
> reasonable options have been exhausted... becase I can't subclass bool, he
> has effectively made the approach as defective as all others... ( eg: Threw
> the baby out with the bath water... though he may have had a good reason to
> do it... )
It would also be pointless, as I noted above. If the subclass only
exists to host static methods, then why is it important that they be
on a subclass?
> It's because Guido van Rosen said that there should never be another
> *instance* of a bool. Now, I'm really agreeing with you -- that I would
> really like to subclass bool, and simply make an instance out of it -- even
> if a limited singleton type ; but Guido said No. And Guido also objected to
> any kind of duck type, although he didn't specifically change the language
> to prevent them ... yet ...
When did Guido ever object to duck typing?
> And most of this thread has been nothing more than me asking "why" did Guido
> say to do that -- and people avoiding answering the question.
Wait, are you actually asking why bool is a doubleton? If nobody has
answered that, I think probably nobody understood you were asking it,
because it shouldn't need to be explained.
Boolean algebra has two values: true and false, or 1 and 0, or humpty
and dumpty, or whatever you like to call them. The bool class
represents the values of boolean algebra. Therefore, there are two of
them. If I have an object that I know is an instance of bool, the
implication is that it is one of those two values, not something
potentially completely different.
Can you name any other language that *does* allow subclassing of
booleans or creation of new boolean values?
> [1] Actually, its metaclass is only type if you inherit from object,
> which you didn't do, although in Python 2.x it must be done
> explicitly. If you don't inherit from object, you get what is known as
> a "classic class" instead of a new-style class. There is no reason to
> use the old-style classes unless you really know what you are doing.
>
> It wastes less memory and invokes less overhead to use the old style
> classes. Since I was making a class with a very narrow purpose, I saw no
> reason to go to the extra work of subclassing from object or type when I
> wasn't going to use the mechanisms from them anyway.
That sounds like premature optimization to me. Always subclassing
object when no more specific class is available would be a good habit
to get into. The savings you're talking about are not that great, it
will save you surprises when an old-style class doesn't do what you
expect, and it will produce code that is more compatible with and more
easily ported to Python 3.
> This seems fine, but I don't get why you're messing around with
> subclassing tuples. Do you really want to have boolean values that are
> iterable, indexable, etc. for no reason? Just create a normal class
> (or maybe subclass int, to match the normal bool class) and let your
> instances contain a tuple instead of being one.
>
> Sorting traditionally allows and uses rich compares when sorting complex
> data. (cf. the email's name) This point was examined earlier in the
> thread by another poster, who suggested I wrap my values in a list to allow
> sort() functions to do a hierarchical compare.
>
> Although I couldn't implement the sort in the way that poster recommended
> for various technical reasons -- none the less, their suggestion led to a
> fruitful study of how and when sort algorithms do comparisons and what they
> expect from them.
>
> The tuple, then, is a natural object to contain an actual instance of bool
> (as opposed to a subclass) and work seamlessly with sorting algorithms
> already available in python.
Which doesn't answer my question at all: why inherit from tuple when
composition would suffice? A boolean is not a tuple, so a boolean
class should not subclass tuple. It sounds to me like you turn to
subclassing as a solution much more quickly than is wise.
As a final note, please only post plain text messages to this
newsgroup, not html. Not everybody can easily read html messages, and
as you can probably see from this post it can mess up things like
reply quoting.
[toc] | [prev] | [next] | [standalone]
| From | Gregory Ewing <greg.ewing@canterbury.ac.nz> |
|---|---|
| Date | 2015-01-16 22:23 +1300 |
| Message-ID | <chs3kpFgp3aU1@mid.individual.net> |
| In reply to | #83792 |
Ian Kelly wrote: > Wait, are you actually asking why bool is a doubleton? If nobody has > answered that, I think probably nobody understood you were asking it, > because it shouldn't need to be explained. What does perhaps need explaining is why Python goes out of its way to *enforce* the doubleton-ness of bool. I don't know all of Guido's reasoning on this, but part of it is probably to do with implementation efficiencies. There are several places in the CPython source where it's assumed that, if an object is known to be of type bool, then its truth or falseness can be determined with a pointer comparison. -- Greg
[toc] | [prev] | [next] | [standalone]
| From | Andrew Robinson <andrew3@r3dsolutions.com> |
|---|---|
| Date | 2015-01-14 23:23 -0800 |
| Message-ID | <mailman.17743.1421306724.18130.python-list@python.org> |
| In reply to | #83661 |
[Multipart message â attachments visible in raw view] — view raw
>> And most of this thread has been nothing more than me asking "why" >> did Guido >> say to do that -- and people avoiding answering the question. > Wait, are you actually asking why bool is a doubleton? If nobody has > answered that, I think probably nobody understood you were asking it, > because it shouldn't need to be explained. In some ways, yes I am asking that -- but in another I am not; to be more precise -- Why did Guido refuse to allow refinement of meaning when in industry -- logic equations generally are not limited to Charles Bool's original work, but include practical enhancements of his theories which make them far more useful. A subclass is generally backward compatible in any event -- as it is built upon a class, so that one can almost always revert to the base class's meaning when desired -- but subclassing allows extended meanings to be carried. eg: A subclass of bool is a bool -- but it can be MORE than a bool in many ways. One example: It can also be a union. So when Guido chose to cut off subclassing -- his decision had a wider impact than just the one he mentioned; eg: extra *instances* of True and False.... as if he were trying to save memory or something. The reason Guido's action puzzles me is twofold -- first it has been standard industry practice to subclass singleton (or n-ton) objects to expand their meaning in new contexts, and this practice has been documented for many years. So -- why did Guido go against the general OOP practice unless he didn't know about it? The mere existence of a subclass does not threaten the integrity of bools or security in Python in any way I can see -- as one can do a type check, or a base type check, on an instance to decide how to handle a subclass vs. base class instance. So I'm guessing he was concerned about something else, but I don't know what. In general -- it's not the goal of subclassing to create more instances of the base types -- but rather to refine meaning in a way that can be automatically reverted to the base class value (when appropriate) and to signal to users that the type can be passed to functions that require a bool because of backward compatibility. > Boolean algebra has two values: true and false, or 1 and 0, or humpty > and dumpty, or whatever you like to call them. You're speaking to an Electrical engineer. I know there are 10 kinds of people, those who know binary and those who don't. But you're way off base if you think most industrial quality code for manipulating boolean logic uses (or even can be restricted to use) only two values and still accomplish their tasks correctly and within a finite amount of time. > The bool class > represents the values of boolean algebra. Therefore, there are two of > them. If I have an object that I know is an instance of bool, the > implication is that it is one of those two values, not something > potentially completely different. > > Can you name any other language that *does* allow subclassing of > booleans or creation of new boolean values? Yes. Several off the top of my head -- and I have mentioned these before. They generally come with the extra subclasses pre-created and the user doesn't get to create the classes, but only use them; none the less -- they have more than two values with which to do logic equations with. VHDL, Verilog, HDL, Silos III, and there are IEEE variants also. C/C++ historically allowed you to do it with instances included, although I am not sure it still does. The third value is usually called "TRI-state" or "don't care". (Though its sometimes a misnomer which means -- don't know, but do care.) Most of these high definition languages are used to do things like design micorprocessors... eg: the very intel or arm processor you typically run python on --- because trying to do it with boolean logic and theorems of the past in a pencil and paper compatible strict re-incarnation of what Charles Bool's did in his own time (even if done by computer) -- rather than including De-morgan and all the many other people who contributed afterward -- is about as red-neck backward as one can get -- and often doomed to failure (though for small applications you might get away with it.) Often, only one extra ( tri state ) value is needed to do logic verification and testing; but in some cases, notably, where the exclusive 'or' function is involved, the relationship between don't care inputs can become important and more values are required; eg: to detect when in deeply nested logic, various sources of don't care inputs interfere with each and themselves in constructive or destructive ways to produce constant logic Trues or Falses. We've discovered that we live in a quantum-mechanical universe -- yet people still don't grasp the pragmatic issue that basic logic can be indeterminate at least some of the time ?! The name 'boolean logic' has never been re-named in honor of the many people who developed the advancements in computers -- including things like data sheets for electronic parts, or the code base used for solving large numbers of simultaneous logic equations with uncertainty included -- which have universally refined the boolean logic meanings found in "Truth" tables having clearly more than two values -- but don't take my word for it -- look in any digital electronics data book, and there they will be more than two states; marked with rising edges, falling edges, X's for don't cares, and so forth. Even though the BASE values are 1 and 0 in all the cases I've mentioned -- there is more to consider. > That sounds like premature optimization to me. Always subclassing > object when no more specific class is available would be a good habit > to get into. The savings you're talking about are not that great, it > will save you surprises when an old-style class doesn't do what you > expect, and it will produce code that is more compatible with and more > easily ported to Python 3. Oh I agree -- but I meant e-mail space. Not just computer memory. I wasn't going to use any of that in the example I showed D'Aprano and didn't want to bother to type it up. The example was only meant to show three things: I -- that a class type can be used to replace an instance variable for a method can be called on a class as opposed to only an instance II -- that types and instances can both be arbitrarily placed in a list to determine their relative lexical sorting order. III -- that both of these things can be done without the class being instantiated. All three of those things were demonstrated in the example -- so it was good enough for my purposes. As to all of your other objections -- hell -- I never intended that example to be used in a real life situation beyond demonstrating the basic feasability of encoding data as a type. As I said to D'Aprano -- even a *cursory* examination (eg: as in not detailed) shows I could do things which he wasn't considering. > >> This seems fine, but I don't get why you're messing around with >> subclassing tuples. Do you really want to have boolean values that are >> iterable, indexable, etc. for no reason? Just create a normal class >> (or maybe subclass int, to match the normal bool class) and let your >> instances contain a tuple instead of being one. >> >> Sorting traditionally allows and uses rich compares when sorting complex >> data. (cf. the email's name) This point was examined earlier in the >> thread by another poster, who suggested I wrap my values in a list to >> allow >> sort() functions to do a hierarchical compare. >> >> Although I couldn't implement the sort in the way that poster >> recommended >> for various technical reasons -- none the less, their suggestion led >> to a >> fruitful study of how and when sort algorithms do comparisons and >> what they >> expect from them. >> >> The tuple, then, is a natural object to contain an actual instance of >> bool >> (as opposed to a subclass) and work seamlessly with sorting algorithms >> already available in python. > Which doesn't answer my question at all: why inherit from tuple when > composition would suffice? A boolean is not a tuple, so a boolean > class should not subclass tuple. It sounds to me like you turn to > subclassing as a solution much more quickly than is wise. > > As a final note, please only post plain text messages to this > newsgroup, not html. Not everybody can easily read html messages, and > as you can probably see from this post it can mess up things like > reply quoting. I don't have a setting on my email to turn off html. Sorry. Can't help. I don't know what you mean about composition vs. sub-classing. Would you care to show an example of how it would solve the problem and still allow hierarchical sorting ? I don't see how you can get pre-existing python functions (like sort, max, and min) to accept a complex bool value and have that value automatically revert to a True or False in an intelligent manner without overloading the operators defined in some class -- somewhere -- to operate on the data the class contains. How do you intend to achieve that with this -- composition -- thing you mentioned ?
[toc] | [prev] | [next] | [standalone]
| From | Steven D'Aprano <steve@pearwood.info> |
|---|---|
| Date | 2015-01-15 08:41 +0000 |
| Message-ID | <54b77d4a$0$2738$c3e8da3$76491128@news.astraweb.com> |
| In reply to | #83799 |
On Wed, 14 Jan 2015 23:23:54 -0800, Andrew Robinson wrote:
[...]
> A subclass is generally backward compatible in any event -- as it is
> built upon a class, so that one can almost always revert to the base
> class's meaning when desired -- but subclassing allows extended meanings
> to be carried. eg: A subclass of bool is a bool -- but it can be MORE
> than a bool in many ways.
You don't have to explain the benefits of subclassing here.
I'm still trying to understand why you think you *need* to use a bool
subclass. I can think of multiple alternatives:
- don't use True and False at all, create your own multi-valued
truth values ReallyTrue, MaybeTrue, SittingOnTheFence, ProbablyFalse,
CertainlyFalse (or whatever names you choose to give them);
- use delegation to proxy True and False;
- write a class to handle the PossiblyTrue and PossiblyFalse cases,
and use True and False for the True and False cases;
There may be other alternatives, but what problem are you solving that
you think
class MyBool(bool): ...
is the only solution?
> One example: It can also be a union.
I don't understand what you think this means. I know what *I* think it
means, but "subclass = union" doesn't make sense to me, so wonder what
you think it means.
> So when Guido chose to cut off
> subclassing -- his decision had a wider impact than just the one he
> mentioned; eg: extra *instances* of True and False.... as if he were
> trying to save memory or something.
*shrug* well maybe he was.
> The reason Guido's action puzzles me is twofold -- first it has been
> standard industry practice to subclass singleton (or n-ton) objects to
> expand their meaning in new contexts,
I dispute that. I *strongly* dispute that.
Industry practice, in my experience, is that there is one and only one
case where you can subclass singleton classes: when you have a factory
which chooses at runtime which subclass to instantiate, after which you
can no longer instantiate any of the other subclasses.
E.g. given an *abstract* class Maze, and three *concrete* subclasses
Labyrinth, OrdinaryMaze, and EnchantedMaze, you can have a factory
function, or method on Maze:
class Maze:
_the_instance = None
@classmethod
def create(cls, variant='ordinary'):
if cls._the_instance is None:
# Create the Maze singleton
if variant == 'ordinary':
cls._the_instance = OrdinaryMaze()
elif variant == 'labyrinth':
cls._the_instance = Labyrinth()
elif variant == 'enchanted':
cls._the_instance = EnchantedMaze()
return _the_instance
(This is just a sketch of a solution, because the caller can bypass the
factory and just call the subclasses directly. In Java it is possible to
write this in such a way that the caller cannot easily do so.)
Why are we limited to a single Maze? Making Maze a singleton in the first
place was a bad idea. The singleton design pattern is *highly* over-used
and abused. But that is another story. Let's just assume that the
designer has good reason to insist that there be only one Maze, perhaps
it uses some resource which truly is limited to there being one only. If
that is the case, then allowing the caller to break that invariant by
subclassing will just lead to horrible bugs. By using a factory and
controlling access to the subclasses, Maze can keep the singleton
invariant and allow subclasses.
This is not relevant to bool, since True and False are already
instantiated.
[...]
> In general -- it's not the goal of subclassing to create more instances
> of the base types
That might not be the goal, but it is the effect. When you instantiate
the subclass, by definition the instances are also instances of the base
classes.
> -- but rather to refine meaning in a way that can be
> automatically reverted to the base class value (when appropriate) and to
> signal to users that the type can be passed to functions that require a
> bool because of backward compatibility.
And I am wondering what you think you can extend bools to perform that is
completely backwards compatible to code that requires bools?
I don't think you can. I think you are engaged on a fool's errand, trying
to do something impossible *even if subclassing bool were allowed*. But I
could be wrong. I just don't think you can possibly write code which is
backwards-compatible with code that expects bools while still extending
it. People are so prone to write:
if flag is True: ...
if flag is False: ...
(which is naughty of them, but what are you going to do?)
[...]
>> Can you name any other language that *does* allow subclassing of
>> booleans or creation of new boolean values?
>
> Yes. Several off the top of my head -- and I have mentioned these
> before. They generally come with the extra subclasses pre-created and
> the user doesn't get to create the classes, but only use them; none the
> less -- they have more than two values with which to do logic equations
> with.
>
> VHDL, Verilog, HDL, Silos III, and there are IEEE variants also. C/C++
> historically allowed you to do it with instances included, although I am
> not sure it still does.
C doesn't have instances because it doesn't have objects. I'm not
certain, but I don't think the other languages you refer to are object-
oriented either. Verilog is a structured programming language, Silos is a
Verilog simulator, and I think VHDL and HDL are versions of Verilog (that
is, I've only seen them written as "Verilog-VHDL" and "Verilog-HDL").
In any case, Verilog *by design* uses four-state logic, modelling 1, 0,
floating, undefined. It is not a bool, since *by definition* bools model
only two states.
> The third value is usually called "TRI-state" or "don't care". (Though
> its sometimes a misnomer which means -- don't know, but do care.)
And SQL has NULL, which makes it an example of tri-state logic. (To be
precise, SQL uses a version of Kleene K3 logic.)
[snip description of modelling hardware circuits]
All very interesting, but completely irrelevant to the question of
subclassing bool.
> We've discovered that we live in a quantum-mechanical universe -- yet
> people still don't grasp the pragmatic issue that basic logic can be
> indeterminate at least some of the time ?!
Of course they do. My first post to you in this thread suggested that
before you start re-inventing the wheel you look at prior art in the
multi-value logic field.
> The name 'boolean logic' has never been re-named in honor of the many
> people who developed the advancements in computers -- including things
> like data sheets for electronic parts,
Are you really suggesting that the name of Boolean Logic should be
renamed away from the person who invented the field and instead named
after the person who first wrote down a list of electronic part numbers
and their specifications?
> or the code base used for solving
> large numbers of simultaneous logic equations with uncertainty included
> -- which have universally refined the boolean logic meanings found in
> "Truth" tables having clearly more than two values -- but don't take my
> word for it -- look in any digital electronics data book, and there they
> will be more than two states; marked with rising edges, falling edges,
> X's for don't cares, and so forth.
And those truth tables are not part of Boolean algebra.
[...]
> As I said to D'Aprano -- even a *cursory* examination (eg: as in not
> detailed) shows I could do things which he wasn't considering.
Andrew, I think you will be surprised at what I have considered. If you
search the archives, you will find that (by memory) a decade ago I had
considered using classes without instantiating them.
The questions I have about your strategy is not what can be done in
Python, but how you think these things you want to do will solve the
problem you apparently have?
To give an analogy... I have no doubt that you can build an television.
But I question how building a television solves your problem of
transporting a goat, a wolf and a cabbage across the river.
[...]
> I don't have a setting on my email to turn off html. Sorry. Can't help.
You are using Thunderbird. You certainly do have such a setting.
> I don't know what you mean about composition vs. sub-classing. Would you
> care to show an example of how it would solve the problem and still
> allow hierarchical sorting ?
Composition is just another name for delegation, which is often used as
the implementation of proxying. In an earlier message, you claimed to be
able to write proxies. Does that help?
> I don't see how you can get pre-existing python functions (like sort,
> max, and min) to accept a complex bool value and have that value
> automatically revert to a True or False in an intelligent manner without
> overloading the operators defined in some class -- somewhere -- to
> operate on the data the class contains.
Here, let me get you started on this:
class TriStateLogic(int):
def __new__(cls, value):
if value in (0, 1): return bool(value)
# Fold all other values to 2.
obj = super(TriStateLogic, cls).__new__(cls, 2)
return obj
def __repr__(self):
return "Undefined"
def __nonzero__(self):
return False
And an example in use:
py> Undefined = TriStateLogic(999)
py> if Undefined:
... print "This is true"
... else:
... print "This is false"
...
This is false
py> sorted([True, False, Undefined, 99, 100])
[False, True, Undefined, 99, 100]
py> sorted([True, False, Undefined, 99, 100], reverse=True)
[100, 99, Undefined, True, False]
--
Steven
[toc] | [prev] | [next] | [standalone]
| From | Andrew Robinson <andrew3@r3dsolutions.com> |
|---|---|
| Date | 2015-01-15 17:45 -0800 |
| Message-ID | <mailman.17777.1421372716.18130.python-list@python.org> |
| In reply to | #83802 |
On 01/15/2015 12:41 AM, Steven D'Aprano wrote:
> On Wed, 14 Jan 2015 23:23:54 -0800, Andrew Robinson wrote:
>
> [...]
>> A subclass is generally backward compatible in any event -- as it is
>> built upon a class, so that one can almost always revert to the base
>> class's meaning when desired -- but subclassing allows extended meanings
>> to be carried. eg: A subclass of bool is a bool -- but it can be MORE
>> than a bool in many ways.
> You don't have to explain the benefits of subclassing here.
>
> I'm still trying to understand why you think you *need* to use a bool
> subclass. I can think of multiple alternatives:
>
> - don't use True and False at all, create your own multi-valued
> truth values ReallyTrue, MaybeTrue, SittingOnTheFence, ProbablyFalse,
> CertainlyFalse (or whatever names you choose to give them);
>
> - use delegation to proxy True and False;
>
> - write a class to handle the PossiblyTrue and PossiblyFalse cases,
> and use True and False for the True and False cases;
>
> There may be other alternatives, but what problem are you solving that
> you think
>
> class MyBool(bool): ...
>
> is the only solution?
That's a unfair question that has multiple overlapping answers.
Especially since I never said subclassing bool is the 'only' solution; I
have indicated it's a far better solution than many.
So -- I'll just walk you through my thought processes and you will see
what I consider problems:
Start with the concept that as an engineer, I have spent well over
twenty years on and off dealing with boolean values that are very often
mixed indistinguishably with 'don't care' or 'tri-state' or 'metastable
states'. A metastable state *is* going to be True or False once the
metastability resolves by some condition of measurement/timing/etc.; but
that value can not be known in advance. eg: similar to the idea that
there is early and late binding in programming.... Sometimes there is a
very good reason to delay making a final decision until the last
possible moment; and it is good to have a default value defined if no
decision is made at all.
So -- From my perspective, Guido making Python go from an open ended and
permissive use of anything goes as a return value that can handle
metastable states -- into to a historical version of 'logic' being
having *only* two values in a very puritanical sense, is rather -- well
-- disappointing. It makes me wonder -- what hit the fan?! Is it
lemmings syndrome ? a fight ? no idea.... and is there any hope of
recovery or a work around ?
eg: To me -- (as an engineer) undefined *IS* equivalent in useage to an
acutal logic value, just as infinity is a floating point value that is
returned as a 'float'. You COULD/CAN separate the two values from each
other -- but always with penalties. They generally share an OOP 'is'
relationship with respect to how and when they are used. (inf) 'IS' a
float value and -- uncertain -- 'IS' a logic value.
That is why I automatically thought before I ever started writing on
this list (and you are challenging me to change...) -- that 'uncertain'
should share the same type (or at least subtype) as Bool.
Mathematicians can argue all they want that 'infinity' is not a float
value, and uncertain is not a True or False. And they are/will be
technically right -- But as a practical matter -- I think programmers
have demonstrated over the years that good code can handle 'infinity'
most efficiently by considering it a value rather than an exception.
And I think the same kind of considerations very very likely apply to
Truth values returned from comparisons found in statistics, quantum
mechanics, computer logic design, and several other fields that I am
less familiar with.
So -- let's look at the examples you gave:
> - don't use True and False at all, create your own multi-valued
> truth values ReallyTrue, MaybeTrue, SittingOnTheFence, ProbablyFalse,
> CertainlyFalse (or whatever names you choose to give them);
>
OK. So -- what do I think about when I see your suggestion:
First I need to note where my booleans come from -- although I've never
called it multi-valued logic... so jargon drift is an issue... though
you're not wrong, please note the idea of muti-value is mildly misleading.
The return values I'm concerned about come from a decimal value after a
comparison with another decimal value.
eg:
a = magicFloat( '2.15623423423(1)' )
b = magicFloat('3()')
myTruthObject = a>b
Then I look at python development historically and look at the built in
class's return values for compares; and I notice; they have over time
become more and more tied to the 'type' bool. I expect sometime in the
future that python may implement an actual type check on all comparison
operators so they can not be used to return anything but a bool. (eg:
I already noticed a type check on the return value of len() so that I
can't return infinity, even when a method clearly is returning an
infinitely long iterator -- such as a method computing PI dynamically.
That suggests to me that there is significant risk in python of having
type checking on all __xx__ methods in the future. ) This inspection is
what points me foremost to saying that very likely, I am going to want a
bool or subtye of it (if possible) as a return type as self defense
against future changes in Python -- although at present, I can still get
away with returning other types if bool turns out to be impossible.
Next, I notice that for compatibility it *is* very desirable that I use
the existing '>' operator, because programmers generally want to be
able to use '>' when they are testing greater than -- and in legacy code
I expect people have exclusively done so -- and I know from past
experience that programmers in general will not be happy with typing
'a.greaterThan(b)' religiously. ( Extend my reasoning to all other
comparison operators.)
It would be worse to use '>' and have it trigger an exception when a
non-bool is encountered to force the programmer to attend to special
metastable states differently; because then the programmer has to write
a compete set of secondary handling routines or a 'try' statement around
a very large number of lines of code and that makes for legacy code
rewriting rather than minor upgrading...
It would also be bad to have my code have modal settings because I don't
want to bother with thread information or have programmers consider
thread issues unless it's a last resort; although that's the approach
used by the Decimal class and other examples I have seen.
So: In general, the most desirable return type is determined by what
python actually returns for normal comparison operations; eg: apparently
a bool -- but with some way of signaling (if the user cares) that more
precise information is available as to why a value is False if it is False.
Unfortunately, the '>', '<', '==', and other operators have no way of
returning additional information on their own; so again, a second
(undesirable) function/method would need to be invoked to overcome the
limitation if only a strict bool is allowed as a return type; and that
means con-concomitant issues of storage and wasted re-computation and
threads.
So, your first alternative is the most at risk of future problems due to
constraints likely to be placed on comparison return types ; and as I
don't want to do much maintenance on my library in the future -- I don't
think that is a very good choice for making my library with.
> - use delegation to proxy True and False;
That sounds like a far more likely to succeed alternative, and is one of
a handful of alternatives I have been exploring on my own.
Proxies allow detection of an actual deterministic False vs. a default
False. So a proxy's id() can signal to a user when it is possible to
upgrade a False to True should they care. Therefore -- If Guido would
see fit to permanently allow proxied True and False values to be
returned in lieu OF an actual True and False value, then this would be a
near ideal alternative. But Python does not implement a general purpose
proxy that I know of ...
I have gotten single instance of a class acting as a proxy to mostly
work; and I have gotten isinstance( myTruthValue, bool ) to return True
for the proxy object -- which is not a bool itself. However, when I
attempt multiple instances of the proxy -- it becomes more difficult. I
think a pure python implementation might be possible -- and I'll
continue to try for a while -- but python may not be able to do it
totally from the python side because there is a difference in how Python
handles type() checks and isinstance() checks.
> - write a class to handle the PossiblyTrue and PossiblyFalse cases,
> and use True and False for the True and False cases;
I very much would want to do as you state here because it would preserve
both True and False unaltered --- which would ALWAYS work in legacy
code; but I don't know how to do it safely.
Although I can use True for absolute Truth -- I can not use False as
absolute False without inviting confusion as to when to allow advanced
compares.
When I do a comparison on any False < False, in legacy code -- it needs
to return False.
But, when looking at uncertainty values, if totally False 'is' the same
as base type False -- then the issue arises that a comparison False <
AnyOtherPartTrueFalse needs to be False for legacy compares but True
for advanced compares;
It's inconsistent and I have no way of detecting where the False I am
comparing with to make a proper decision.
So: The only solution I see is to assume that whenever a uncertainty is
compared against a legacy bool -- that the legacy style of comparison is
absolutely required for safety; and a second version of False must be
defined to detect when the compare needs to take uncertainty into account.
All of these issues are handled correctly in the example tuple class I
already showed. So the tuple class I showed is presently the best
solution with the most compatability that I have found so far.
>
>
>> One example: It can also be a union.
> I don't understand what you think this means. I know what *I* think it
> means, but "subclass = union" doesn't make sense to me, so wonder what
> you think it means.
It's a fringe use-case in Python that I don't think many people use/know
about. I was just being thorough in listing it.
I haven't seen it used in actual python code myself -- but I know from
the literature I've read on Python that it is occasionally used in a
manner analogous to that of C/C++.
In C/C++ unions are a datatype that allow two (or more) different types
of data to be defined as held by one object, but only one of them is
allowed to be initialized at a time because their location in computer
memory which overlaps. C places no restrictions on the compatibility of
the datatypes -- which is different than Python, but Python has a
similar ability.
In Python, when multiple inheritance is invoked -- it does some kind of
check on the base types for compatibility; but still appears to be able
/ or simply does overlap the allocated memory for the different base
types; eg: according to several sources I have read (at least on
C-python internals).
So one can semantically instantiate a subclass of one subtype without
semantically instantiating the other.
ALl I know about it is what I have seen it in Python literature -- and I
tested the examples I was shown to see if they still work, and they do
-- and noted that at least at one time Guido apparently thought it was a
good idea ; but I haven't pursued it beyond that.
>> So when Guido chose to cut off
>> subclassing -- his decision had a wider impact than just the one he
>> mentioned; eg: extra *instances* of True and False.... as if he were
>> trying to save memory or something.
> *shrug* well maybe he was.
:) LOL. I don't have any real idea.... but it would be useful to know
for sure.
>
>> The reason Guido's action puzzles me is twofold -- first it has been
>> standard industry practice to subclass singleton (or n-ton) objects to
>> expand their meaning in new contexts,
> I dispute that. I *strongly* dispute that.
>
> Industry practice, in my experience, is that there is one and only one
> case where you can subclass singleton classes: when you have a factory
> which chooses at runtime which subclass to instantiate, after which you
> can no longer instantiate any of the other subclasses.
OK.
Well, I'll just say that I believe you -- and I'm not really sure what
you're objecting to in what I said -- but if a singleton subclass /
factory existed for my purpose -- I would be happy to choose it at
runtime just like your maze guys do...! If Guido would do that... he
would give me a subtype of bool and that would be very nice indeed.
But dreams aside -- I still note your admission shows that industry does
allow subclassing of singletons even if it requires the owner of the
singleton (Guido) to allow the subtypes.
Cf: Design Patters, Elements of Reusable Object Oriented Software (
Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides ) pp. 127
"Chapter, Singleton -- Object Creational"
--------------------------------------------------------
-- Applicability:
Use the singleton pattern when:
-- There must be exactly one instance of a class, and it must be
accessible to clients from a well known access point.
-- When the sole instance should be extensible by subclassing, and
clients should be able to use an extended instance without modifying
their code.
...
Consequences:
The singleton pattern has several benefits:
...
4. Permits a variable number of instances. The pattern makes it easy
to change your mind and allow more than one instance of the Singleton
Class. Moreover, you can use the same approach to control the number of
instances that the application uses. Only the operation that grants
access to the singleton instance needs change.
...
Implementation:
2. Subclassing the Singleton Class: The main issue is not so much
defining the subclass but installing it's unique instance so that
clients will be able to use it.
...
A more flexible approach uses a registry of singletons. Instead of
having Instance define the set of possible Singleton Classes, The
Singleton classes can register their singleton instance by name in a
well known registry.
...
Of course, the constructor won't get called unless someone instantiates
the class, which echoes the problem the Singleton is trying to solve.
---------------------------------------------------------
> Why are we limited to a single Maze? Making Maze a singleton in the first
> place was a bad idea. The singleton design pattern is *highly* over-used
> and abused. But that is another story. Let's just assume that the
> designer has good reason to insist that there be only one Maze, perhaps
> it uses some resource which truly is limited to there being one only. If
> that is the case, then allowing the caller to break that invariant by
> subclassing will just lead to horrible bugs.
Right -- when a user does not know the reason for a singleton ; breaking
it is just ASKING for bugs. I agree. That's why I have been asking
about why Guido did it... there are times to avoid breaking the rules,
and times to crush them.
> By using a factory and
> controlling access to the subclasses, Maze can keep the singleton
> invariant and allow subclasses.
>
> This is not relevant to bool, since True and False are already
> instantiated.
There's nothing stopping Guido from making it relevant...
>
> [...]
>> In general -- it's not the goal of subclassing to create more instances
>> of the base types
> That might not be the goal, but it is the effect. When you instantiate
> the subclass, by definition the instances are also instances of the base
> classes.
All right -- I can agree to that and will concede that point -- as I
don't see much purpose in pursuing it further as I suspect (without
proof) that Guido might not like that extra instances of class
definitions that I might use as a work-around... although I don't really
know why it's so important to him.
>> -- but rather to refine meaning in a way that can be
>> automatically reverted to the base class value (when appropriate) and to
>> signal to users that the type can be passed to functions that require a
>> bool because of backward compatibility.
> And I am wondering what you think you can extend bools to perform that is
> completely backwards compatible to code that requires bools?
I've never said 'completely compatible', and have been very careful not
to make extremest remarks.
I want to get as close as I can to fully backward compatible -- and am
willing to put some time into it rather than taking the first solution
that vaguely works...
> I don't think you can. I think you are engaged on a fool's errand, trying
> to do something impossible *even if subclassing bool were allowed*. But I
> could be wrong. I just don't think you can possibly write code which is
> backwards-compatible with code that expects bools while still extending
> it. People are so prone to write:
>
> if flag is True: ...
> if flag is False: ...
D'Aprano -- I think your making what is known as a straw man argument.
Refer back to your earlier suggestion of re-using True and False to
represent themselves, and some other type to represent the intermediate
metastates; From your remark here, I surmise that you must have already
figured out that the alternative you gave me was never meant to work --
otherwise it would solve the very problem you now present me with for
any case where my numbers are identical in meaning with legacy numbers
-- eg: it *would* work perfectly for any truly legacy application. The
failures -- would show up with new applications or non legacy data which
could erroneously trigger a legacy compare when it ought not do so
because you can not get the new types returned unless non-legacy data
has been encountered.
> (which is naughty of them, but what are you going to do?)
>
Nothing, except hope that the people who wrote Python itself didn't do
anything naughty in sort() min() and max() and friends. So far my tests
show that they didn't. But -- you're right -- Non core language
implementation programmers, are going to have occasional bugs that
either they or I will have to hunt down, depending on who it is that
needs their software to work with my library.
> C doesn't have instances because it doesn't have objects. I'm not
> certain, but I don't think the other languages you refer to are object-
> oriented either. Verilog is a structured programming language, Silos is a
> Verilog simulator, and I think VHDL and HDL are versions of Verilog (that
> is, I've only seen them written as "Verilog-VHDL" and "Verilog-HDL").
OOP programming in C is not done using formal class keywords, etc, but
it is done by defining structs and compiler modules and pointers to
functions; So -- C -- doesn't have the security measures that a C++
compiler implements for OOP ( 'private' ,'protected' ); but OOP can
still be done in C including inheritance. 'C' most certainly does have
instances and singletons. Several packages available under GPL, such as
the GTK widget set, are implemented in strict C (not C++) and as full
object oriented packages, then another optional package can be compiled
if C++ bindings to the objects are desired.
Verilog is the originator of the language family I mentioned, yes; and
they are all variations on a theme -- but there are versions of HDL's by
other companies, and the US government; Most are based on C syntax,
some are based on ADA, and other languages that engineers happen to like
for various applications; etc. I mentioned only the most used versions.
> In any case, Verilog *by design* uses four-state logic, modelling 1, 0,
> floating, undefined. It is not a bool, since *by definition* bools model
> only two states.
Not quite, verilog is meant to handle two state logic. AKA: Binary bit
or Boolean, and to also work with metastable data; eg: In electronics,
floating or unknown or oscillating or frozen between states for a period
of time while settling are traditionally called metastable. I am not
sure if this is a mathematican's definition, or if it's because these
quasi-states were defined with/after (meta) the two stable ones. It's
something I will have to check. But I remember from my early college
courses that it is technically wrong to call them all states, even
though 'don't care' is often referred to as the tri-state.
In any event -- Your comment about verilog still just demonstrates that
Guido has downgraded python's return types into a more more primitive
system than is warranted by the history of the creation of the
computer. Verilog (1984) existed before Python (1989) so *even*
verilog's conventions predate python's. And I don't even remember when
HiLo used to be around but I'm sure it's older than verilog. So -- from
the very beginning of Python, design logic for boolean systems has
*always* included meta-state information with boolean values.
>> The third value is usually called "TRI-state" or "don't care". (Though
>> its sometimes a misnomer which means -- don't know, but do care.)
> And SQL has NULL, which makes it an example of tri-state logic. (To be
> precise, SQL uses a version of Kleene K3 logic.)
OK. I agree -- it does.
>
> [snip description of modelling hardware circuits]
> All very interesting, but completely irrelevant to the question of
> subclassing bool.
No, not really -- but I'll respect your difference of opinion.
I'm getting the message that the reason Guido though this was important
was because the historical meaning of bool is more important than the
idea that metastability is an issue to be dealt with at the same time as
the data value -- like electrical engineers do regularly.
>
>> We've discovered that we live in a quantum-mechanical universe -- yet
>> people still don't grasp the pragmatic issue that basic logic can be
>> indeterminate at least some of the time ?!
> Of course they do. My first post to you in this thread suggested that
> before you start re-inventing the wheel you look at prior art in the
> multi-value logic field.
Did you ? -- Did I reply to that e-mail? I'm not sure I read it...
But the word is different from what I am used to -- eg: meta-stable
logic 'states' ... ?
Now that I'm looking up words -- I see that wikipedia is calling the
indeterminate states 'multi-value'; I'm getting old... I am used to the
term metastable; not multi-value. Weird. Jargon problems...
Even so -- I seriously don't think of Quantum mechanics as multi-value ;
it's uncertain and 'collapses' to a definite value when measured. I can
understand your intention now... I'll have to go back and search for the
old email. My apology.
>
>
>> The name 'boolean logic' has never been re-named in honor of the many
>> people who developed the advancements in computers -- including things
>> like data sheets for electronic parts,
> Are you really suggesting that the name of Boolean Logic should be
> renamed away from the person who invented the field and instead named
> after the person who first wrote down a list of electronic part numbers
> and their specifications?
Nope.
Though I DO want to point out that Charles Bool did not invent the
computer, build the microprocessor, or any of the things which would
give a logical reason why his more archaic *usage* is given preference
over the useage preferred by the very people who DID invent the
microprocessor, computer, and programming languages.
Name recognition is great for honoring a man -- but makes for a poor
reason to choose a strict implementation of bool.
>
>> or the code base used for solving
>> large numbers of simultaneous logic equations with uncertainty included
>> -- which have universally refined the boolean logic meanings found in
>> "Truth" tables having clearly more than two values -- but don't take my
>> word for it -- look in any digital electronics data book, and there they
>> will be more than two states; marked with rising edges, falling edges,
>> X's for don't cares, and so forth.
> And those truth tables are not part of Boolean algebra.
Oh wow!!!! I never expected to hear that -- But I guess you were never
trained to do boolean algebra, formally ? Or did you mean something else ?
http://en.wikipedia.org/wiki/Truth_table
The truth tables on data sheets are VERY VERY much intended to be
related to boolean logic.
Electronic engineers routinely put the words "Truth table" on datasheets
where the boolean information is recorded (and I'm sure even on relay
logic prior to the vaccum tube) but still add x's because *as
inventors* they knew Bool's usage wasn't enough to convey ideas
efficiently and fully.
It's pragmatism over rigid formalism left over from an age where the
computer as we have it was not even conceived.
http://www.eleccircuit.com/cd4027b_datasheet-of-dual-j-k-flip-flop/
> [...]
>> As I said to D'Aprano -- even a *cursory* examination (eg: as in not
>> detailed) shows I could do things which he wasn't considering.
> Andrew, I think you will be surprised at what I have considered. If you
> search the archives, you will find that (by memory) a decade ago I had
> considered using classes without instantiating them.
No surprise. I know from reading your work that you have been doing
programming a long time, and have fairly well substantiated / reasonable
opinions even if I disagree with some of them as trying to overemphasize
to a fault a definition which has never been honored in the past by
those who used it most.
> The questions I have about your strategy is not what can be done in
> Python, but how you think these things you want to do will solve the
> problem you apparently have?
>
> To give an analogy... I have no doubt that you can build an television.
> But I question how building a television solves your problem of
> transporting a goat, a wolf and a cabbage across the river.
Ask away. I already have one solution that works reasonably well, the
tuple rich compare;
So it's not like I don't have a solution -- it's just that I'm not sure
that I can't do better.
If Python had never added the bool definition to the language, I
wouldn't even have to bother with any of this supposed 'fools' errand
nuisance in the first place... but I'll make the best of it.
> [...]
>> I don't have a setting on my email to turn off html. Sorry. Can't help.
> You are using Thunderbird. You certainly do have such a setting.
It's nice to know that you read and believe what you see in an email header.
Note: Headers are sometimes modified by sysadmins who actually care
about security.
PPS: If there is a way to turn off HTML in this email program -- it is
not obvious -- and I have looked.
I've done my best not to push any HTML enhancement buttons...
[toc] | [prev] | [next] | [standalone]
| From | Steven D'Aprano <steve+comp.lang.python@pearwood.info> |
|---|---|
| Date | 2015-01-16 18:33 +1100 |
| Message-ID | <54b8bedf$0$12986$c3e8da3$5496439d@news.astraweb.com> |
| In reply to | #83847 |
Andrew Robinson wrote:
[...much about bools...]
I'll get back to that shortly, but for now I just want to make a couple of
comments about your HTML email.
>> [...]
>>> I don't have a setting on my email to turn off html. Sorry. Can't help.
>> You are using Thunderbird. You certainly do have such a setting.
>
> It's nice to know that you read and believe what you see in an email
> header. Note: Headers are sometimes modified by sysadmins who actually
> care about security.
No they're not. They're modified by sysadmins who like to give the false
impression that they care about security. Also who don't care about your
privacy. (I wouldn't trust a sys admin who modified the headers my mail
program set. I would wonder what else he is modifying behind my back.)
Faking the Useragent header is as useful for security as removing the badge
off a Toyota and replacing it with a Ford badge. Even if you can come up
with some conceivable set of circumstances where you are more secure ("what
if a terrorist with a grudge against Toyota sees your car and is
sufficiently enraged to blow it up???"), it's just security theatre.
To quote Whitfield Diffie:
"The secret to strong security: less reliance on secrets."
If the security of your email program depends on people being misinformed
about which email program you have, then you have no little or no security.
> PPS: If there is a way to turn off HTML in this email program -- it is
> not obvious -- and I have looked.
> I've done my best not to push any HTML enhancement buttons...
Despite your belief that you are unable to disable HTML in your email, I can
tell you that out of the twelve posts I have received from you so far,
eight have no HTML (including this one of yours which I am replying to). So
you can disable it -- the only trick is to work out what you are doing, or
not doing, to avoid triggering it.
(If you are using Thunderbird, it gives you extensive control over when HTML
mail is set. Apart from being able to disable it completely, you can set
certain domains to only receive plain text emails. Look under Preferences
or Settings or whatever it's called now -- you're an engineer. I'm sure
you'll work it out.)
--
Steven
[toc] | [prev] | [next] | [standalone]
| From | Gregory Ewing <greg.ewing@canterbury.ac.nz> |
|---|---|
| Date | 2015-01-17 00:07 +1300 |
| Message-ID | <chs9nsFi9ogU1@mid.individual.net> |
| In reply to | #83847 |
Andrew Robinson wrote:
>
> I never said subclassing bool is the 'only' solution; I
> have indicated it's a far better solution than many.
An assertion with which we very much disagree.
> I have spent well over
> twenty years on and off dealing with boolean values that are very often
> mixed indistinguishably with 'don't care' or 'tri-state' or 'metastable
> states'.
I think you're overestimating how useful it will be to
pass one of your "extended boolean" values to existing
code expecting a plain boolean.
The purpose of the bool type in Python and other languages
is for making control-flow decisions. When you hit
if x:
do_something()
else:
do_something_else()
and x is Undefined or TriState or "47% true", what is
supposed to happen? The right thing to do will depend on
the circumstances, so you're going to need custom code
for dealing with those values.
I'm not even sure it's right to single out two of the
extended values as corresponding to True and False in
all cases. It may seem obvious that the "high" state
of a digital logic signal should be True and the "low"
state should be False, but -- what about active-low
signals? They use the opposite convention!
> Then I look at python development historically and look at the built in
> class's return values for compares; and I notice; they have over time
> become more and more tied to the 'type' bool.
Actually, it's the opposite. Originally, all the comparison
operators got funneled through a single special method __cmp__,
which was required to return a negative, zero or positive
integer; hard-coded logic in the interpreter then derived a
boolean from that.
When rich comparisons were introduced (i.e. the ability to
override all the comparison operators individually), that
restriction was lifted.
> I expect sometime in the
> future that python may implement an actual type check on all comparison
> operators so they can not be used to return anything but a bool.
That's not going to happen. If nothing else, it would
break NumPy, which compares arrays element-by-element and
returns an array of booleans.
> I already noticed a type check on the return value of len() so that I
> can't return infinity, even when a method clearly is returning an
> infinitely long iterator
That's acknowledged as being less than desirable, but
fixing it would have performance implications, as well as
breaking the C extension API.
It's not really much of a limitation, anyway. Iterators
don't actually have a __len__; they can optionally have a
__length_hint__ method, but infinite iterators can just
leave that undefined.
> That suggests to me that there is significant risk in python of having
> type checking on all __xx__ methods in the future.
You need have no fear of that. The trend has actually
been towards *less* restrictions on return types, and
I can't imagine that changing.
>> - use delegation to proxy True and False;
>
> That sounds like a far more likely to succeed alternative, and is one of
> a handful of alternatives I have been exploring on my own.
A proxy is still a different type. Whether you use a
proxy or a completely new type for this is entirely a
matter of implementation convenience. You won't magically
gain any capabilities that you couldn't have implemented
otherwise.
> python may not be able to do it
> totally from the python side because there is a difference in how Python
> handles type() checks and isinstance() checks.
There's no way I know of to make type() lie about the
true type of an object, proxy or not. But that would only
be a problem for code relying on type(x) is bool, which
I expect to be extremely rare if it exists at all.
> Though I DO want to point out that Charles Bool did not invent the
> computer,
That's correct, because he didn't exist. :-) Boolean algebra
is named after George Boole. You're probably thinking of
Charles Babbage, who design (although never fully implemented)
what could be regarded as the first general purpose programmable
computer.
> Name recognition is great for honoring a man -- but makes for a poor
> reason to choose a strict implementation of bool.
This is utter nonsense. The "strict" implementation of
bool you speak of isn't chosen to honour George Boole.
It's chosen because it's perfectly suited for its intended
application of flow-control in programming -- which is the
vast majority of the use of logic *above* the hardware
level. And since it happens to be a true Boolean algebra,
the name is entirely appropriate.
>> And those truth tables are not part of Boolean algebra.
>
> Oh wow!!!! I never expected to hear that -- But I guess you were never
> trained to do boolean algebra, formally ?
A Boolean algebra is a mathematical structure with a precise
definition. There exist Boolean algebras with more than two
values, but the don't-care states found in data sheets don't
fit into any of them. The data sheets call them "truth tables",
not "Boolean algebra tables". :-)
> The truth tables on data sheets are VERY VERY much intended to be
> related to boolean logic.
Related, yes. Boolean algebra is a part of those truth tables,
not the other way around. What Steven said is correct.
--
Greg
[toc] | [prev] | [next] | [standalone]
| From | Rustom Mody <rustompmody@gmail.com> |
|---|---|
| Date | 2015-01-16 03:22 -0800 |
| Message-ID | <3d5ed0d2-d27d-4282-9581-64e1a33610bd@googlegroups.com> |
| In reply to | #83847 |
On Friday, January 16, 2015 at 7:20:13 AM UTC+5:30, Andrew Robinson wrote:
<snipped 542 lines>
Disclaimers
1. Ive not really read the above 542 lines and earlier
2. I am not a fan of OOP
Still some thoughts...
Electrical engineering (EE) and computer science (CS) may seem related
but are quite different disciplines. In fact there is some amount of
'client-supplier' in this relation - you folks make the machines we use.
Now one of the basic things that needs to be effected to make this transition
is the so-called digital abstraction
To start with we say (say) that 0V is 0-logic, 3.3V is 1-logic.
But that's hardly enough, we need margins, forbidden regions, Postel's law
etc. This mapping is hardly straightforward. And that is still the
'static-discipline'.
When time comes in we need to deal with the fact that when a gate
switches it will willy-nilly go through the forbidden region. From here
we have to go through/into clock disciplines, delay insensitie circuits etc.
Should CS-ists deal with all this??
If you say yes then what are you EE-guys doing?
If no then you are agreeing with all the others here.
In some more detail:
You seem to want a multi-valued logic. How many values?
There are quite a few answers:
- 4 -- {0,1,Z,X} - http://en.wikipedia.org/wiki/Four-valued_logic
- 9 -- above + weak drives http://en.wikipedia.org/wiki/IEEE_1164
And probably half a dozen others.
You say you REALLY NEED these in your work.
Yes, many people need many things, eg.
1. Mars orbiter was lost due to a mismatch of MKS and FPS systems
http://edition.cnn.com/TECH/space/9909/30/mars.metric.02/
Does that make a case for building in units into programming languages?
2. C.A.R Hoare said the invention of the null-pointer was a billion-dollar
mistake. Do C programmers agree with him?
3. He also considered exception handling as a terrible disaster since it
confuses flow of control. Are python (or most modern language) users likely
to agree?
All these are instances of a basic principle that Niklaus Wirth enunciated:
The most important decision of a language designer are what to leave out
of the language.
Finally in python 3.4 onwards there are enums. You can do this
>>> from enum import IntEnum
>>> class Bool4(IntEnum):
... F=0
... T=1
... Z=2
... X=3
[toc] | [prev] | [next] | [standalone]
| From | Steven D'Aprano <steve+comp.lang.python@pearwood.info> |
|---|---|
| Date | 2015-01-17 23:27 +1100 |
| Message-ID | <54ba552a$0$13007$c3e8da3$5496439d@news.astraweb.com> |
| In reply to | #83847 |
Andrew, sorry for the delay in responding. Your response has been extremely
long, so for the interest of brevity I've tried to trim things down to the
most pertinent points.
Andrew Robinson wrote:
[...]
> So -- From my perspective, Guido making Python go from an open ended and
> permissive use of anything goes as a return value that can handle
> metastable states
That absolutely has not changed. You seem to be mixing the concept of
*subclassing* with the concept of being able to handle multi-state
(metastable) logic. You can certainly write multi-state logic in Python.
> -- into to a historical version of 'logic' being
> having *only* two values in a very puritanical sense, is rather -- well
> -- disappointing. It makes me wonder -- what hit the fan?! Is it
> lemmings syndrome ? a fight ? no idea.... and is there any hope of
> recovery or a work around ?
Do you have much programming experience outside of the niche of modelling
electronic hardware?
I can tell you that it is standard for general purpose programming languages
to be restricted to two-state (Boolean) comparisons. The basic decision
construct is "if...else", which is based on two states.
The only exception that I know of is the very early, and long deprecated,
Fortran "Arithmetic IF" statement:
IF (e) label1, label2, label3
which did a three-way jump depending on whether e was less than zero, equal
to zero, or greater than zero. Some assemblers also included three-way
jumps.
All forms of N-valued logic can be reduced to Boolean logic with appropriate
comparisons. Instead of trying to come up with syntax for a three-way
branch:
if flag: print True
else: print False
otherwise: print Indeterminate
and four-way branches, a five-way branches, ... ad infinitum, it is easier
to leave it up to the programmer to build N-way logic on top of 2-way
True/False comparisons:
# equality here returns True or False
if flag == True: print True
else:
if flag == False: print False
else: print Indeterminate
Some programming languages add syntax to make this more friendly:
if flag == True: print True
elif flag == False: print False
else: print Indeterminate
or even:
case flag of:
True: ...
False: ...
Indeterminate: ...
Maybe: ...
Uncertain: ...
Could_Be: ...
(for languages with a case or switch statement).
Not only can all N-way logics be mathematically generated from Boolean
algebra, but I'll go further and say that *almost certainly* the hardware
simulation languages you are used to with 3-way or 4-way logic use 2-way
Boolean logic under the hood.
The point of this is that you need not think of Guido "taking away"
anything. It is normal for programming languages to use Boolean
comparisons.
But in fact Python is *less* restrictive than many other languages. In
Pascal, for instance, comparisons like <= or > etc. can only return true or
false, while Python allows them to return anything.
> eg: To me -- (as an engineer) undefined *IS* equivalent in useage to an
> acutal logic value, just as infinity is a floating point value that is
> returned as a 'float'. You COULD/CAN separate the two values from each
> other -- but always with penalties. They generally share an OOP 'is'
> relationship with respect to how and when they are used. (inf) 'IS' a
> float value and -- uncertain -- 'IS' a logic value.
That's fine, we have no dispute with the need for multi-state logics. But
*which* multi-state logic? There are *at least* three useful ternary
logics: Kleene logic, Ćukasiewicz logic, Bochvar logic. And then there is
Belnap's four-valued relevance logic (True, False, Both True and False,
Neither True nor False), and an infinite number of other possible logics.
Which should the programming language support?
In a sense, any Turing Complete programming language supports *all of them*.
All you need to do is program the details yourself. But in the sense of
which multi-state logic should the language itself include, I think that
the answer is ... none of them. Most programming languages have no need to
include this complexity, just as most languages don't include tensors as a
language primitive.
I guess this is just a long-winded way of me saying that HDLs are
effectively a "Domain Specific Language" for hardware, and can *and should*
contain specialist semantics needed by hardware designers, e.g. 4-way
logic. But Python is a general purpose language, and while you *can*
program your own domain specific features, you have to do so within the
constraints of the needs of a general-purpose language. In Python's case,
those constraints are actually remarkably loose (not as loose as languages
like Lisp or Forth, where you can literally redesign the interpreter on the
fly!) but they do exist.
[...]
> So -- let's look at the examples you gave:
>
>> - don't use True and False at all, create your own multi-valued
>> truth values ReallyTrue, MaybeTrue, SittingOnTheFence, ProbablyFalse,
>> CertainlyFalse (or whatever names you choose to give them);
>>
> OK. So -- what do I think about when I see your suggestion:
>
> First I need to note where my booleans come from -- although I've never
> called it multi-valued logic... so jargon drift is an issue... though
> you're not wrong, please note the idea of muti-value is mildly misleading.
I accept that jargon is an issue, but I believe that "N-way logic"
and "multi-value logic" is, if not standard, at least something that we can
agree to use. The use of "boolean" to describe anything with more than two
states is just weird.
I don't understand what you mean by "note where my booleans come from".
> The return values I'm concerned about come from a decimal value after a
> comparison with another decimal value.
> eg:
>
> a = magicFloat( '2.15623423423(1)' )
> b = magicFloat('3()')
>
> myTruthObject = a>b
>
> Then I look at python development historically and look at the built in
> class's return values for compares; and I notice; they have over time
> become more and more tied to the 'type' bool. I expect sometime in the
> future that python may implement an actual type check on all comparison
> operators so they can not be used to return anything but a bool.
That will never happen. Comparison operators are explicitly documented as
being allowed to return anything that you wish. Changing that will break so
much code, including important, high-profile libraries like numpy, that it
is never going to occur even if somebody wanted it to.
> (eg:
> I already noticed a type check on the return value of len() so that I
> can't return infinity, even when a method clearly is returning an
> infinitely long iterator
Yes, that's a known issue, and you're not the first to notice it. That's
partly due to history, and partly to disagreements about API design.
> Next, I notice that for compatibility it *is* very desirable that I use
> the existing '>' operator, because programmers generally want to be
> able to use '>' when they are testing greater than -- and in legacy code
> I expect people have exclusively done so -- and I know from past
> experience that programmers in general will not be happy with typing
> 'a.greaterThan(b)' religiously. ( Extend my reasoning to all other
> comparison operators.)
Don't worry, customising comparison operators for your own classes is fully
supported.
[...]
> So: In general, the most desirable return type is determined by what
> python actually returns for normal comparison operations; eg: apparently
> a bool -- but with some way of signaling (if the user cares) that more
> precise information is available as to why a value is False if it is
> False.
Forget the "apparently a bool" part. Comparison operators can return
anything you like. If you have a good reason to return a bool, feel free,
but you are not required too.
You could, should you so choose, implement a complete fuzzy logic system:
py> class C(object):
... def __eq__(self, other):
... return 0.5
...
py> c = C()
py> c == 23
0.5
Although you cannot attach extra information to built-in floats like 0.5,
you can certainly create your own classes and attach extra information to
their instances.
> Unfortunately, the '>', '<', '==', and other operators have no way of
> returning additional information on their own;
They can return any value you like. Perhaps the biggest, most important
third-party library in the Python ecosystem uses this extensively:
py> import numpy
py> a = numpy.array([1, 2, 3, 4, 5, 6])
py> b = numpy.array([2, 1, 3, 6, 5, 4])
py> a < b
array([ True, False, False, True, False, False], dtype=bool)
py> a == b
array([False, False, True, False, True, False], dtype=bool)
If numpy can return an array, you can return a MaybeTrue or ProbablyFalse
object, with whatever diagnostic information you require.
(You will also note that numpy arrays don't inherit from bool.)
> So, your first alternative is the most at risk of future problems due to
> constraints likely to be placed on comparison return types ;
"Most at risk" in this case equals zero risk. Comparison operators will not
be restricted to only returning bools. That will never happen.
>> - write a class to handle the PossiblyTrue and PossiblyFalse cases,
>> and use True and False for the True and False cases;
>
> I very much would want to do as you state here because it would preserve
> both True and False unaltered --- which would ALWAYS work in legacy
> code; but I don't know how to do it safely.
>
> Although I can use True for absolute Truth -- I can not use False as
> absolute False without inviting confusion as to when to allow advanced
> compares.
>
> When I do a comparison on any False < False, in legacy code -- it needs
> to return False.
That's trivial. Give your Maybe objects __lt__ methods that return False
when compared to False. Here is a stub to get you started:
def __lt__(self, other):
if other is False:
return False
elif other == ...
> But, when looking at uncertainty values, if totally False 'is' the same
> as base type False -- then the issue arises that a comparison False <
> AnyOtherPartTrueFalse needs to be False for legacy compares but True
> for advanced compares;
What's a "legacy compare"? What's "advanced compares"? It seems to me that
you are inventing difficulties with Python that don't exist, and then
creating complex solutions to work around the non-existent problem.
I'm going to take a guess here...
You have "fuzzy decimals", and when you compare two fuzzy decimals, you want
to return something like "equal", "completely less than", "partially less
than" and so on. For simplicity, I'm only going to discuss the less than
operator, but the same could apply to any comparison operator:
a < b can return:
True -- means that a certainly is less than b
False -- means that a certainly is not less than b
MostlyTrue -- means that a is probably less than b
MostlyFalse -- means that a is probably not less than b
So *fuzzy decimals* have to compare by returning four (or three, or five, or
however many you choose) tetra-logic (tri-logic, quin-logic...) values. To
implement this, you define a __lt__ method on the fuzzy decimal class,
together with the other comparison operators.
But the tetra-logic values themselves shouldn't compare fuzzily with each
other. You should define a fixed order for them, most likely:
False < MostlyFalse < MostlyTrue < True
and have comparison operators on the tetra-logic values themselves return
True or False:
False < True --> returns True
MostlyFalse < True --> returns True
However, should you disregard my advice and decide that MostlyFalse is only
*mostly* larger than False, but is also *slightly* equal to False, you can
certainly have the tetra-logic objects return *themselves* when compared. I
advise against it, not because it is hard to do in Python, but because I
think it will be hard for people to understand. But it can certainly be
done, by defining a __lt__ method on the tetra-logic class itself.
The only thing you can't do in Python is to override the boolean operators
`or` and `and` to implement three-value or four-value logic, e.g. you
cannot have this:
MostlyFalse and True --> MostlyTrue # or whatever rule you prefer
There have been proposals to allow overriding `and` and `or`, but so far
they have all run into difficulties with semantics, backwards
compatibility, efficiency, or all three.
I can only suggest that you accept that the `and` and `or` operators will
implicitly coerce their arguments into boolean True/False, and if you want
fuzzy operators, define some functions which do what you want:
def and_(a, b): ...
def or_(a, b): ...
A small inconvenience.
> It's inconsistent and I have no way of detecting where the False I am
> comparing with to make a proper decision.
I don't understand why you think you need to detect where False is. Where in
what sense? Objects in Python don't have a spacial location.
> So: The only solution I see is to assume that whenever a uncertainty is
> compared against a legacy bool -- that the legacy style of comparison is
> absolutely required for safety; and a second version of False must be
> defined to detect when the compare needs to take uncertainty into account.
I don't understand this.
[...]
>> Industry practice, in my experience, is that there is one and only one
>> case where you can subclass singleton classes: when you have a factory
>> which chooses at runtime which subclass to instantiate, after which you
>> can no longer instantiate any of the other subclasses.
>
> OK.
> Well, I'll just say that I believe you -- and I'm not really sure what
> you're objecting to in what I said -- but if a singleton subclass /
> factory existed for my purpose -- I would be happy to choose it at
> runtime just like your maze guys do...! If Guido would do that... he
> would give me a subtype of bool and that would be very nice indeed.
You cannot choose True and False at runtime because they are required by the
language. You cannot say "I don't wish to use True and False as my bools, I
wish to instantiate this alternate class instead and use CouldBe and
BuckleysChance as the two bools." That cannot work because long before your
code gets to run and create the subclass, Python has already instantiated
and used True and False. Functions are documented to return True or False,
not "some arbitrary bool subclass instances". The interpreter *cannot work*
without two known values for True and False, so replacing them at runtime
is impossible.
Could Python have been designed differently? Well yes, of course it could
have. If it were designed to be like PHP or Smalltalk or C++ then it would
have been just like PHP or Smalltalk or C++, but it wasn't. Python has the
design it has now, and some changes (like allowing the user to select which
two bools are used at runtime) are simply too extensive a change to be
practical. A bit like changing a three-story house built on a concrete slab
into a two story house on wooden foundations with a deep cellar, without
tearing the house down and starting again from scratch.
> But dreams aside -- I still note your admission shows that industry does
> allow subclassing of singletons even if it requires the owner of the
> singleton (Guido) to allow the subtypes.
And you are free to allow subclassing of your own singletons. Feel free to
allow an million instances of your singleton class, Python won't stop you
from shooting yourself in the foot if it is your own class.
[...]
>> I don't think you can. I think you are engaged on a fool's errand, trying
>> to do something impossible *even if subclassing bool were allowed*. But I
>> could be wrong. I just don't think you can possibly write code which is
>> backwards-compatible with code that expects bools while still extending
>> it. People are so prone to write:
>>
>> if flag is True: ...
>> if flag is False: ...
>
> D'Aprano -- I think your making what is known as a straw man argument.
I am sorry if I have misunderstood your position, but believe me, I am
responding to what I understand your position to be as accurately and
fairly as I can. But honestly Andrew, I believe that either your design is
confused and confusing, or you have failed to explain it well enough for me
to see that it is not confused.
> Refer back to your earlier suggestion of re-using True and False to
> represent themselves, and some other type to represent the intermediate
> metastates; From your remark here, I surmise that you must have already
> figured out that the alternative you gave me was never meant to work --
> otherwise it would solve the very problem you now present me with for
> any case where my numbers are identical in meaning with legacy numbers
This is exactly the sort of confusion I'm talking about.
Logically, your numbers (I presume you are talking about the fuzzy decimals
you mentioned earlier, with some sort of uncertainty or error-bars
attached) cannot possibly be identical in meaning to legacy numbers (i.e.
regular decimals).
There is no logical sense that "number plus or minus uncertainty" can be
*identical* to "number alone", even if the uncertainty is zero. Because the
fuzzy decimals will have an uncertainty but the regular decimals will not.
If you have methods for dealing with that uncertainty, regular decimals
won't have those methods. If they have an "uncertainty" attribute, regular
decimals won't have one. If they print in such a way to show that
uncertainty, regular decimals will not.
If they have *identical* behaviour to regular decimals, then they merely
*are* regular decimals with a different (and likely slower or buggy)
implementation.
None of this is to say that you cannot have fuzzy decimals interoperate with
regular decimals, according to some rules which define how they should
combine. E.g. by treating regular numbers as exact, or giving them some
default uncertainty.
[...]
>>> or the code base used for solving
>>> large numbers of simultaneous logic equations with uncertainty included
>>> -- which have universally refined the boolean logic meanings found in
>>> "Truth" tables having clearly more than two values -- but don't take my
>>> word for it -- look in any digital electronics data book, and there they
>>> will be more than two states; marked with rising edges, falling edges,
>>> X's for don't cares, and so forth.
>>
>> And those truth tables are not part of Boolean algebra.
>
> Oh wow!!!! I never expected to hear that -- But I guess you were never
> trained to do boolean algebra, formally ? Or did you mean something else ?
>
> http://en.wikipedia.org/wiki/Truth_table
>
> The truth tables on data sheets are VERY VERY much intended to be
> related to boolean logic.
*Related to* boolean logic, but not *of* boolean logic. It is a
generalisation of boolean logic to more than two states, in a similar way
that complex numbers (3+2j where j = sqrt -1) are extended from the real
numbers. As an electrical engineer, I expect you are familiar with complex
numbers, and I hope that you would also understand that complex numbers are
not part of the Reals.
In any case, while the use of 3- and 4-valued logic is all very interesting
and important to electrical engineering, it is very much domain-specific
and is not so useful for a general purpose programming language. People
have difficulty enough dealing with SQL NULLs and floating point NANs.
You can write your own multi-valued logic in Python, just don't expect
Python to support it natively. Python is not a HDL.
--
Steven
[toc] | [prev] | [next] | [standalone]
Page 1 of 3 [1] 2 3 Next page →
Back to top | Article view | comp.lang.python
csiph-web