Python 3 built-in functions and classes - python

When I am creating a list using below statement
a = list('jane')
Am I calling Python's built-in list function or instantiating list class.
My understanding is we are instantiating list class by passing 'jane' as argument.
However, the Python's documentation https://docs.python.org/3/library/functions.html says list() is built-in function.

The docs explicitly say:
class list([iterable])
Rather than being a function, list is actually a mutable sequence type
You can easily check that:
>>> type(list)
type
if it was a function, function would be the output provided by using type.
You're instantiating a list object the same way you'd do if you created your own class and called it. type's __call__ is essentially getting invoked and sets up your instance so, though they aren't a function per se, they are callable.
The fact that they are listed in that specific section is probably for convenience, it might be confusing but reading the description of it is supposed to disambiguate this.

Your question is answered by the very documentation page you mention:
class list([iterable])
Rather than being a function, list is actually a mutable sequence type, as documented in Lists and Sequence Types — list, tuple, range.
In Python, both classes and functions are callable, so in practice, you can treat them alike.

You are instantiating a list.
class list([iterable])
Rather than being a function, list is actually a mutable sequence type, as documented in Lists and Sequence Types — list, tuple, range.

Related

Pythonic way to distinguish read-only function input parameters from mutable ones

Python doesn't seem to have a valid const qualifier per How do I create a constant in Python? What would be the most "pythonic" way for differentiating read-only / mutable function parameters? Should I just point it out in the comments?
# my_graph is READ-ONLY
# my_set is added items with property X ...
def my_lovely_function(my_graph,my_set):
In C and several other languages, typically real outputs are communicated via inputs that are passed by pointers or by reference. The reason for this is that the return value mechanism in many of these paradigms has been hijacked for error handling purposes i.e. return value is a success/error code while useful outputs are populated in an input/pointer reference. This has led for the need to denote some inputs as being untouchable (consts) and others as being touchable to prevent confusion in using the function.
Python typically doesn't want you to do things that way. It wants you to use Exceptions and exception handling for error handling and to use return statements for actual outputs. This is cleaner and more in line with the original idea of return values before they were highjacked by error handling.
In some cases, it is still more convenient to use a mutable input to transfer data out. Everything in python is always by reference. This is fine except if the calling context doesn't want you, the function, to modify the variable it provided as an input.
Python's solution is to 1) expect the function writer to properly document inputs, outputs, and side-effects on mutable inputs, and 2) provide the calling context the option of passing in immutable objects if they want to ensure those objects will not be changed.
So if you have a list and you don't want some function you call to add or subtract things from it, pass in the information as a tuple instead. No function will be able to add or subtract anything to your tuple, however they might be able to change elements of the tuple if those are mutable. Instead of a set, pass a frozenset. There is no immutable dict type, but you can get around that by passing a copy or translating it to a frozenset of tuples. Strings, ints, floats, and complex numbers are all immutable. Note that mutable objects embedded in immutable containers can still be changed. If this is undesired, then make sure they are immutable. Alternatively, if you are paranoid, you can call copy.deepcopy() on an object to make a totally independent copy (recursively) to pass into the function. Any changes at any nested level of this deep copy will not affect the original object.
When writing a function, it should be clear from the documentation (preferably docstring) or the code itself what the return values and side effects on mutable objects are. Using docstrings to capture this when writing a function is best practice.
When calling a function, you should defensively make use of immutable types (or deep copying if need be) as needed for your specific circumstances.

Is `list()` considered a function?

list is obviously a built-in type in Python. I saw a comment under this question which calls list() a built-in function. And when we check the documentation, it is, indeed, included in Built-in functions list but the documentation again states:
Rather than being a function, list is actually a mutable sequence type
Which brings me to my question: Is list() considered a function? Can we refer to it as a built-in function?
If we were talking about C++, I'd say we are just calling the constructor, but I am not sure if the term constructor applies to Python (never encountered its use in this context).
list is a type, which means it is defined somewhere as a class, just like int and float.
>> type(list)
<class 'type'>
If you check its definition in builtins.py (the actual code is implemented in C):
class list(object):
"""
Built-in mutable sequence.
If no argument is given, the constructor creates a new empty list.
The argument must be an iterable if specified.
"""
...
def __init__(self, seq=()): # known special case of list.__init__
"""
Built-in mutable sequence.
If no argument is given, the constructor creates a new empty list.
The argument must be an iterable if specified.
# (copied from class doc)
"""
pass
So, list() is not a function. It is just calling list.__init__() (with some arguments which are irrelevant for this discussion) just like any call to CustomClass() is doing.
Thanks for #jpg for adding in the comments: classes and functions in Python have a common property: they are both considered as callables, which means they are allowed to be invoked with (). There is a built-in function callable that checks if the given argument is callable:
>> callable(1)
False
>> callable(int)
True
>> callable(list)
True
>> callable(callable)
True
callable is also defined in builtins.py:
def callable(i_e_, some_kind_of_function): # real signature unknown; restored from __doc__
"""
Return whether the object is callable (i.e., some kind of function).
Note that classes are callable, as are instances of classes with a
__call__() method.
"""
pass
When you call list(), you're invoking the constructor of the list class (list.__init__).
If you have any doubt about the use of the term "constructor" in Python, this is the exact word that the implementers of list chose to refer to __init__:
https://github.com/python/cpython/blob/master/Objects/listobject.c#L2695

Why method accepts class name and name 'object' as an argument?

Consider the following code, I expected it to generate error. But it worked. mydef1(self) should only be invoked with instance of MyClass1 as an argument, but it is accepting MyClass1 as well as rather vague object as instance.
Can someone explain why mydef is accepting class name(MyClass1) and object as argument?
class MyClass1:
def mydef1(self):
return "Hello"
print(MyClass1.mydef1(MyClass1))
print(MyClass1.mydef1(object))
Output
Hello
Hello
There are several parts to the answer to your question because your question signals confusion about a few different aspects of Python.
First, type names are not special in Python. They're just another variable. You can even do something like object = 5 and cause all kinds of confusion.
Secondly, the self parameter is just that, a parameter. When you say MyClass1.mydef1 you're asking for the value of the variable with the name mydef1 inside the variable (that's a module, or class, or something else that defines the __getattr__ method) MyClass1. You get back a function that takes one argument.
If you had done this:
aVar = MyClass1()
aVar.mydef1(object)
it would've failed. When Python gets a method from an instance of a class, the instance's __getattr__ method has special magic to bind the first argument to the same object the method was retrieved from. It then returns the bound method, which now takes one less argument.
I would recommend fiddling around in the interpreter and type in your MyClass1 definition, then type in MyClass1.mydef1 and aVar = MyClass1(); aVar.mydef1 and observe the difference in the results.
If you come from a language like C++ or Java, this can all seem very confusing. But, it's actually a very regular and logical structure. Everything works the same way.
Also, as people have pointed out, names have no type associated with them. The type is associated with the object the name references. So any name can reference any kind of thing. This is also referred to as 'dynamic typing'. Python is dynamically typed in another way as well. You can actually mess around with the internal structure of something and change the type of an object as well. This is fairly deep magic, and I wouldn't suggest doing it until you know what you're doing. And even then you shouldn't do it as it will just confuse everybody else.
Python is dynamically typed, so it doesn't care what gets passed. It only cares that the single required parameter gets an argument as a value. Once inside the function, you never use self, so it doesn't matter what the argument was; you can't misuse what you don't use in the first place.
This question only arises because you are taking the uncommon action of running an instance method as an unbound method with an explicit argument, rather than invoking it on an instance of the class and letting the Python runtime system take care of passing that instance as the first argument to mydef1: MyClass().mydef1() == MyClass.mydef1(MyClass()).
Python is not a statically-typed language, so you can pass to any function any objects of any data types as long as you pass in the right number of parameters, and the self argument in a class method is no different from arguments in any other function.
There is no problem with that whatsoever - self is an object like any other and may be used in any context where object of its type/behavior would be welcome.
Python - Is it okay to pass self to an external function

In Python, when should an object be passed as an argument as opposed to calling the method on object with the dot operator?

I understand that the dot operator is accessing the method specific to an object that is an instance of the class containing that method/function. However, in which cases do you instead call the function directly on an object, in the form func(obj) as opposed to obj.func()?
Can both techniques always be implemented (at least in custom code) or are there certain cases in which the former should be used over the latter, and vice versa?
I had previously read that the form func(obj) is for processing data that the object holds, but why would this not be possible with doing obj.dataMember.func(), is there an advantage to passing just the object, such as some change in mutability?
If the function exists exclusively to serve that object type, then you should probably make it a method of the class; that requires the obj.func() syntax.
If the function will also work on objects not of that one class, then you should make it a regular function, performing the generalization and discrimination with the function. This requires the syntax func(obj).

Tuple of objects that, if referenced directly, returns first object

I want to modify one of the API methods which returns an object. I think it should return a tuple of objects. I don't want to change the way people call this API method. Is there any way to return a tuple of objects that can be referenced directly for the first object?
If you change a function that returned an object to instead return a tuple, then the callers of the function will have to be changed. There is no way around that. Either you change the callers to extract just the first object, or the unchanged code will have a tuple where it used to have an object.

Categories

Resources