Skip to main content
gamedev.net
🔒 Locked

Python type checking?(reloaded)

Started by mikeman Dec 18, 2010 at 9:06 AM 9 replies 2.6k views
Original Post
mikeman
mikeman
I think I have a somewhat cool idea on how to make "static" type checking work a bit on Python. Consider this:

def MoveShip(ship,x,y):    ....code....    ship.posX=x;    ship.posY=y;    ....code....


Now, if "ship" does not have a "posX" attribute, the function will throw an exception in the middle of it.

Now this:

def MoveShip(ship,x,y):    x=float(x);    y=float(Y);    ship=Ship(ship);     ...code...    ship.posX=x;    ...code....


It seems to me this solves various problems:

1)Does "pre-emptive" type checking before the actual function begins to execute
2)Automatically converts "compatible" types
3)You get easier "smart complete" in various editors, whereas it would be more difficult to get it in local variables with uknown types.

There is of course the issue of having to create a new object, so the method should be treaded with some moderation.

What do you think?
aeroz
aeroz
In your solution, the function will throw an exception too. (maybe you omitted the try block for simplicity)

I suggest not to check the type of the parameters. All you have to do is say that 'ship' needs to provide the posX and posY attributes. Then somebody can use your function with any type that is compatible with ship.

You could also do:
def MoveShip(ship,x,y):    try:        ship.posX=x;    except AttributeError:        raise ValueError('ship needs to have a posX attribute')
You don't really need that because the exception you get without the try block is almost the same anyway.

But I don't have many experience with Python, so maybe this is not the best solution.
mikeman
mikeman
Quote:
Original post by aeroz
In your solution, the function will throw an exception too. (maybe you omitted the try block for simplicity)

I suggest not to check the type of the parameters. All you have to do is say that 'ship' needs to provide the posX and posY attributes. Then somebody can use your function with any type that is compatible with ship.

But I don't have many experience with Python, so maybe this is not the best solution.


Yeah, with Python, the problem is not whether or not it will throw the exception on type-mismatch(I want it to throw), but when. In my example(the second source), it throws whatever type-mismatch on the arguments immediately, otherwise, the whole function could run for some time(even minutes, depending on the program) and then crash. It's a problem I have had with Python several times, personally.
the_edd
the_edd
I guess you already realize that this eliminates one of the reasons for choosing Python in the first place (duck typing).

FWIW, if you really want to check that something is of a certain type, I would just do this:

assert isinstance(ship, Ship)


If something isn't of the expected type, it's likely a programmer error after all.
mikeman
mikeman
Quote:
Original post by the_edd
I guess you already realize that this eliminates one of the reasons for choosing Python in the first place (duck typing).

FWIW, if you really want to check that something is of a certain type, I would just do this:

assert isinstance(ship, Ship)


If something isn't of the expected type, it's likely a programmer error after all.


Actually, duck typing has stopped being my primary reason for choosing Python these days.

But I guess you have a point, yeah. I'll try it out in my new project and see, thanks.
ibebrett
ibebrett
i don't really support this idea, but you could make it a decorator

def static_type(*types):	def wrap(f):		def new(*args):			real_args = [t(v) for t, v in zip(types, args)]			return f(*real_args)		return new	return wrap


and then define your functions like this
@static_type(float, float, str)def do_the_thing(ship, x, y):	print(ship.__class__, x.__class__, y.__class__)	do_the_thing(1, 1.0, 1)alternatively, you could have the code do a test and assert like this:def static_type(*types):	def wrap(f):		def new(*args):			for t, v in zip(types, args):                             assert(isinstance(v, t)) # i forget the ordering on isintanct - look it up 			return f(*real_args)		return new	return wrap
Fenrisulvur
Fenrisulvur
Quote:
Original post by aeroz
I suggest not to check the type of the parameters. All you have to do is say that 'ship' needs to provide the posX and posY attributes. Then somebody can use your function with any type that is compatible with ship.

Duck typing has its uses, but as a universal practice it's a terrible idea. Robust software development should leverage every reasonable formal verification measure available, which is pretty much limited to compile-time type checking; and further, static (particularly manifest) typing results in definite, intrinsic and local documentation of the structures operated on by a routine, and hence a significant sanity check in complicated projects.
Also worth noting is that explicit restrictions of type are often crucial in the development of secure APIs for untrusted code.

In response to these points I often get complaints about lengthy manifest declarations breaking a programmer's flow - but if you're writing robust software, why the hell does this matter? You're meant to be a methodical drone implementing a software design, not hacking away as though you're flying a friggen fighter jet.
Paramount to writing good code is clearly defining data structures and processes, and making the relevant details explicit.

Bluntly, the lack of optional manifest typing in Python is a mistake, because one must resort to this kind of cruft:

def MoveShip(ship,x,y):    assert isinstance(x, float)    assert isinstance(y, float)    assert isinstance(ship, Ship)    # ...


That is, if you feel bringing the roof down with an assertion failure is an acceptable response to a type mismatch, which it probably isn't. This is to say nothing of the inviability of basic automated type verification.

The manifest version is obviously far more eloquent:

def MoveShip(Ship ship, float x, float y):    # ...


EDIT: actually, the decorator above is a bit of an improvement. Still not what it should be, and it should be a standard feature; but it's better than a pile of isinstance calls.
ibebrett
ibebrett
Quote:
Original post by Fenrisulvur
Quote:
Original post by aeroz
I suggest not to check the type of the parameters. All you have to do is say that 'ship' needs to provide the posX and posY attributes. Then somebody can use your function with any type that is compatible with ship.

Duck typing has its uses, but as a universal practice it's a terrible idea. Robust software development should leverage every reasonable formal verification measure available, which is pretty much limited to compile-time type checking; and further, static (particularly manifest) typing results in definite, intrinsic and local documentation of the structures operated on by a routine, and hence a significant sanity check in complicated projects.
Also worth noting is that explicit restrictions of type are often crucial in the development of secure APIs for untrusted code.

In response to these points I often get complaints about lengthy manifest declarations breaking a programmer's flow - but if you're writing robust software, why the hell does this matter? You're meant to be a methodical drone implementing a software design, not hacking away as though you're flying a friggen fighter jet.
Paramount to writing good code is clearly defining data structures and processes, and making the relevant details explicit.

Bluntly, the lack of optional manifest typing in Python is a mistake, because one must resort to this kind of cruft:

def MoveShip(ship,x,y):    assert isinstance(x, float)    assert isinstance(y, float)    assert isinstance(ship, Ship)    # ...


That is, if you feel bringing the roof down with an assertion failure is an acceptable response to a type mismatch, which it probably isn't. This is to say nothing of the inviability of basic automated type verification.

The manifest version is obviously far more eloquent:

def MoveShip(Ship ship, float x, float y):    # ...


I totally disagree with you. I am a professional python programmer and issues of this nature have never come up where I work (which has an admittedly small dev team, so admittedly miscommunications are probably far less likely). Code which checks whether a variable is a type instance usually is not futureproof even slightly.
ibebrett
ibebrett
Quote:
Original post by ibebrett
Quote:
Original post by Fenrisulvur
Quote:
Original post by aeroz
I suggest not to check the type of the parameters. All you have to do is say that 'ship' needs to provide the posX and posY attributes. Then somebody can use your function with any type that is compatible with ship.

Duck typing has its uses, but as a universal practice it's a terrible idea. Robust software development should leverage every reasonable formal verification measure available, which is pretty much limited to compile-time type checking; and further, static (particularly manifest) typing results in definite, intrinsic and local documentation of the structures operated on by a routine, and hence a significant sanity check in complicated projects.
Also worth noting is that explicit restrictions of type are often crucial in the development of secure APIs for untrusted code.

In response to these points I often get complaints about lengthy manifest declarations breaking a programmer's flow - but if you're writing robust software, why the hell does this matter? You're meant to be a methodical drone implementing a software design, not hacking away as though you're flying a friggen fighter jet.
Paramount to writing good code is clearly defining data structures and processes, and making the relevant details explicit.

Bluntly, the lack of optional manifest typing in Python is a mistake, because one must resort to this kind of cruft:

def MoveShip(ship,x,y):    assert isinstance(x, float)    assert isinstance(y, float)    assert isinstance(ship, Ship)    # ...


That is, if you feel bringing the roof down with an assertion failure is an acceptable response to a type mismatch, which it probably isn't. This is to say nothing of the inviability of basic automated type verification.

The manifest version is obviously far more eloquent:

def MoveShip(Ship ship, float x, float y):    # ...


I totally disagree with you. I am a professional python programmer and issues of this nature have never come up where I work (which has an admittedly small dev team, so admittedly miscommunications are probably far less likely). Code which checks whether a variable is a type instance usually is not futureproof even slightly.


add: in addition, types in python can be generated at run time (which for us happens occasionally. You can't compile time check those.
Fenrisulvur
Fenrisulvur
Quote:
Original post by ibebrett
small dev team

Eh, this is more-or-less it. Your focus is on smaller projects, likely from within a more freeform development lifecycle and with an emphasis on pragmatism. Bugs are probably by-and-large tolerable, no? I, on the other hand, am preoccupied with robust, correct software, and find strong static type guarantees very useful.

Nevertheless, outright removing explicit type declarations on variables in a language constitutes an extreme and unjustified stance in language design.

Quote:
Original post by ibebrett
Code which checks whether a variable is a type instance usually is not futureproof even slightly.

That's a pretty strong claim, one which I can't really reconcile in general.

Quote:
Original post by ibebrett
add: in addition, types in python can be generated at run time (which for us happens occasionally. You can't compile time check those.

I think you're demonstating a lack of familiarity with how such patterns are handled in stricter languages, and the benefits offered by providing clear contracts denoting the properties to be implemented by a dynamically introduced type for it to function properly throughout a system. My experience is with Java - pulling in concrete types dynamically only requires very minimal reflection, and the overall approach is far more rigorous and robust than that seen in scripting languages.

Mind, dynamic code generation, outside of some really specialized areas, usually turns up as a byproduct of some sort of bad design or coding practice. You should look long and hard at the sort of things you're generating at runtime, if a more mundane solution would do the job then they're probably an unneeded complication.

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.