I have this line of python code that returns a list of all classes that currently exist:
'a'.__class__.__mro__[1].__subclasses__()
Now I'd expect that this line would work in the same way:
'{0.__class__.__mro__[1].__subclasses__()}'.format('a')
but this gives me the error:
AttributeError: type object 'object' has no attribute '__subclasses__()'
Then again this line:
'{0.__class__.__mro__[1].__subclasses__}'.format('a')
prints out
'<built-in method __subclasses__ of type object at 0x9d1260>'
so the methods seems to be there but I can't call it for some reason. Can somebody explain this behavior to me?
str.format doesn't support arbitrary expressions in format strings. You can use indexing and attribute access, but not the function call operator (and even indexing is a little weird). Rather than trying to stuff all that in the format string itself, evaluate the expression outside and pass the result as an argument to format:
'{}'.format('a'.__class__.__mro__[1].__subclasses__())
Incidentally, this __subclasses__ call doesn't give you a list of all subclasses that exist. It gives you a list of all direct subclasses of object. It doesn't include grandchild classes or further descendants.
Also, unless you're trying to perform some kind of sandbox escape or you've got some other weird constraint, you don't need to go through the whole 'a'.__class__.__mro__[1] rigmarole just to refer to object.
You were accessing an attribute "__subclass__()" (string!) instead of accessing __subclass__ function then call it. i.e. the format string doesn't execute the function and returns the result in string.
To prove, try something instead:
>>> '{0.__class__.__mro__[1].__str__}'.format('a')
"<slot wrapper '__str__' of 'object' objects>"
>>> '{0.__class__.__mro__[1].__str__()}'.format('a')
AttributeError: type object 'object' has no attribute '__str__()'
As stated, str.format does not allow calls.
If you really desire such behaviour, you could try out f-strings (if you are python 3.6+):
>>> x = 'a'
>>> f'{x.__class__.__mro__[1].__subclasses__()}'
# output omitted
Related
Just trying to figure out what the difference is between .type() method and type() function. To me, I don't see why they are returning different outputs although they seem to be doing the same thing (ie. determining the type of the object)
x = 1
print(x.type())
returns: AttributeError: 'int' object has no attribute 'type'
whereas
x = 1
print(type(x))
returns: <class 'int'>
Anyone have any ideas?
There is no such thing as a .type() method for int. That is what the first message is saying. So use the second version and you are fine.
type() is a built-in function, which returns the type of the object you pass in as the parameter.
When you try to call x.type(), what you're attempting to do is call the type() method on an int object, and that simply does not exist. Hence the attribute error.
Note, to check the attributes that actually exist for x, you could use dir(x)
The type(object) function returns the class an object is an instance of. For example, type(42) tells you that 42 is an int.
If you were to make a custom class, using type(object) where object is an instance of the new class would return the class itself.
You can also look into using the isinstance function, which is widely considered to be more Pythonic.
The .type() method doesn’t exist, at lease not for ints. That’s why you get an error there.
Is saying:
if not callable(output.write):
raise ValueError("Output class must have a write() method")
The same as saying:
if type(output.write) != types.MethodType:
raise exceptions.ValueError("Output class must have a write() method")
I would rather not use the types module if I can avoid it.
No, they are not the same.
callable(output.write) just checks whether output.write is callable. Things that are callable include:
Bound method objects (whose type is types.MethodType).
Plain-old functions (whose type is types.FunctionType)
partial instances wrapping bound method objects (whose type is functools.partial)
Instances of you own custom callable class with a __call__ method that are designed to be indistinguishable from bound method objects (whose type is your class).
Instances of a subclass of the bound method type (whose type is that subclass).
…
type(output.write) == types.MethodType accepts only the first of these. Nothing else, not even subclasses of MethodType, will pass. (If you want to allow subclasses, use isinstance(output.write, types.MethodType).)
The former is almost certainly what you want. If I've monkeypatched an object to replace the write method with something that acts just like a write method when called, but isn't implemented as a bound method, why would your code want to reject my object?
As for your side question in the comments:
I do want to know if the exceptions.ValueError is necessary
No, it's not.
In Python 2.7, the builtin exceptions are also available in the exceptions module:
>>> ValueError is exceptions.ValueError
True
In Python 3, they were moved to builtins along with all the other builtins:
>>> ValueError is builtins.ValueError
True
But either way, the only reason you'd ever need to refer to its module is if you hid ValueError with a global of the same name in your own module.
One last thing:
As user2357112 points out in a comment, your solution doesn't really ensures anything useful.
The most common problem is almost certainly going to be output.write not existing at all. In which case you're going to get an AttributeError rather than the ValueError you wanted. (If this is acceptable, you don't need to check anything—just call the method and you'll get an AttributeError if it doesn't exist, and a TypeError if it does but isn't callable.) You could solve that by using getattr(output, 'write', None) instead of output.write, because None is not callable.
The next most common problem is probably going to be output.write existing, and being callable, but with the wrong signature. Which means you'll still get the same TypeError you were trying to avoid when you try to call it. You could solve that by, e.g., using the inspect module.
But if you really want to do all of this, you should probably be factoring it all out into an ABC. ABCs only have built-in support for checking that abstract methods exist as attributes; it doesn't check whether they're callable, or callable with the right signature. But it's not that hard to extend that support. (Or, maybe better, just grabbing one of the interface/protocol modules off PyPI.) And I think something like isinstance(output, StringWriteable) would declare your intention a lot better than a bunch of lines involving getattr or hasattr, type checking, and inspect grubbing.
As far as I understand, everything in python is an object or a reference.
For example: in x = 1, x is a reference to the integer object 1. If I write print type(x), then Python will tell me the object that x is referencing is an integer.
So what about statements such as if?
if I try print type(if), unsurprisingly, I get a syntax error. I can speculate on why this is the case. Maybe if is a static method of a class, or maybe it has somehow been weirdly defined as non returnable, etc. I just don't know.
Ultimately, I suspect that if has nothing to do with an object or a reference. However, that would surely go against the idea of everything being an object or a reference?
When they say "everything is an object or a reference" they are referring specifically to data. So this naturally does not apply to statements. Of course, all expressions will result in data. For example a == b is <class 'bool'> because it is an expression.
There are some languages where if is an expression but python is not one of them.
Say I want to debug a simple class with an attribute myattribute. I create a repr method like this:
class SimpleClass:
def __repr__(self):
return "{0.myattribute}".format(self)
It feels a bit redundant, so I would prefer to use format directly:
class SimpleClass:
__repr__ = "{0.myattribute}".format
...but that fails with an IndexError: tuple index out of range. I understand it that format cannot access the self argument, but I do not see why.
Am I doing something wrong, is this a CPython limitation – or what else?
"{0.myattribute}".format is already a bound method on the string object ("{0.myattribute}"). So when the calling code attempts to look up, say, x.__repr__ (where x is a SimpleClass instance), Python finds the __repr__ attribute of SimpleClass, but then cannot recognize it as a SimpleClass method - the descriptor protocol is not honoured (the string method has no __get__ attribute).
It appears that in 3.4, using a lambda will work, although I could have sworn it had to be a real function in previous versions. functools.partial will not work. But you really should be using a real function anyway. Sorry it's not as DRY as you'd like.
I think I might have a fundamental misunderstanding of what a python attribute actually is. Consider the following:
>>> class Test:
... pass
...
>>> t = Test()
>>> setattr(t, '0', 0)
>>> t.0
File "<stdin>", line 1
t.0
^
SyntaxError: invalid syntax
>>> getattr(t, '0')
0
>>> setattr(t, 'one', 1)
>>> t.one
1
>>> getattr(t, 'one')
1
Why does Python allow me to set an attribute if I can't legally access it with dot notation? I understand that t.0 makes no sense, but at the same time I wonder why it's no different than t.one because I created them the same way.
Attributes are a kind of members any Python object can have. Usually you would expect the built-in syntax to dictate what kind of attribute names are accepted. For that, the definition is pretty clear:
attributeref ::= primary "." identifier
So what follows after the dot is required to be a valid identifier which limits the allowed attribute names easily. Ignoring other Unicode areas for now, it essentially means that the attribute may not start with a number. So 0 is not a valid identifier and as such t.0 is not a valid attribute reference as per the specification.
However, getattr and alike work a bit differently. They just require the attribute name to be a string. And that string is passed on directly to the internal PyObject_GetAttr functions. And those don’t require a valid identifier.
So using getattr etc., you can essentially trick Python and attach attribute to objects, which names would not be allowed according to the specification of the language.
This is just a quirk of the syntax and semantics of python. Any string can be used as an attribute name, however only identifiers can be used with dot notation. Thus the only way of accessing non-identifier attributes is with getattr/setattr or some other indirect function. Strangely enough this practice doesn't extend so far as to allow any type to be an attribute, only strings get that privilege.