Iterator and Generator versions of Python's range()
First of all, to validate that the function is working,
it's good to use assert statements:

assert [0, 1, 2, 3, 4] == [x for x in irange(5)]
assert [2, 3, 4] == [x for x in irange(2, 5)]
assert [2, 1, 0, -1, -2] == [x for x in irange(2, -3, -1)]


With these statements covering my back,
I refactored your irange method to this:

def irange(*args):
    len_args = len(args)

    if len_args &gt; 3:
        raise TypeError('irange() expected at most 3 arguments, got %s' % len_args)

    if len_args &lt; 1:
        raise TypeError('irange() expected at least 1 arguments, got %s' % len_args)

    sanitized_args = [int(x) for x in args]

    if len_args == 1:
        start_element = 0
        end_element = sanitized_args[0]
        step = 1
    else:
        start_element = sanitized_args[0]
        end_element = sanitized_args[1]
        step = 1 if len_args == 2 else sanitized_args[2]

    current = start_element

    if step &gt; 0:
        def should_continue():
            return current &lt; end_element
    else:
        def should_continue():
            return current &gt; end_element

    while should_continue():
        yield current
        current += step


Points of improvement:


Since len(args) is used repeatedly, I cache it in len_args
Added len(args) &lt; 1 check too, in the same fashion as len(args) &gt; 3
Simplified the type checking of args:

Sanitize with a single, simple list comprehension
If there are any non-integer arguments, a ValueError will be raised with a reasonably understandable error message

Simplified the initialization of start_element, end_element and step
Greatly simplified the stepping logic


As for the iterator version,
it would be easiest and best to implement that in terms of the generator version.