I'm taking an AI / ML online course, and some of the assignments include Python language phrases like:
input_img = tf.keras.Input(shape=input_shape)
Z1 = tfl.Conv2D(8, kernel_size=4,strides=(1,1), padding='SAME')(input_img)
It seems that the general syntax of the second line is:
obj1 = <class method>(<method parameters>)(obj2),
where obj1, obj2 are some class instances.
Could not find an explanation to this syntax.
Please direct me to a reference / example clarifying said syntax.
The syntax is that tfl.Conv2D(8, kernel_size=4,strides=(1,1), padding='SAME') returns something that is callable, like a callable object OR a method, then you call it with input_img params
A simple example
class A:
def __init__(self, *args):
self.values = list(args)
def run(self, *more_values):
print(f"A.run with {self.values} - {more_values}")
class B:
#staticmethod
def get_method(a, b, c):
return A(a, b, c).run
In the case B.get_method() returns a run method that is not called, so when retrieving it, you can call it, and split in 2 steps to understand better
B.get_method(1, 2, 3)(4, 5) # A.run with [1, 2, 3] - (4, 5)
my_method = B.get_method(1, 2, 3)
my_method(4, 5) # A.run with [1, 2, 3] - (4, 5)
Related
I have a PointND class representing a point in N-dimensional space (shown below).
class PointND:
"""
Class to represent an N-dimensional point.
"""
def __init__(self, *vals):
if len(vals) == 1 and isinstance(vals, list):
self.vals = vals[0]
else:
self.vals = vals
self.point = []
self.dimensions = len(self.vals)
for i in self.vals:
self.point.append(i)
This works fine when I give it a series of inputs (ex: PointND(1, 2, 3, 4)). However, whenever I give it a list of inputs (ex: PointND([1, 2, 3, 4])) it breaks.
I have the __repr__ function defined as:
def __repr__(self):
__a = f"{self.dimensions}D Point: ({self.vals[0]}"
for i in range(1, len(self.vals)):
__a = f"{__a}, {self.vals[i]}"
__a = f"{__a})"
return __a
And when I do print(PointND(1, 2, 3, 4, 5)), it outputs:
> 4D Point: (1, 2, 3, 4)
However when I simply change it to: print(PointND([1, 2, 3, 4, 5])), it outputs:
> 1D Point: ([1, 2, 3, 4])
I don't know why it does this, but if anyone knows and/or has a fix, please let me know!
Here you probably meant to check the type of vals[0], not vals.
def __init__(self, *vals):
- if len(vals) == 1 and isinstance(vals, list):
+ if len(vals) == 1 and isinstance(vals[0], list):
self.vals = vals[0]
else:
self.vals = vals
(vals will never be a list, it will always be a tuple)
I would argue that this is a bad interface: it lets you express the same thing in two different ways. I would pick one way of creating a point:
# a) accept varargs
def __init__(self, *vals):
self.point = list(vals)
self.dimensions = len(vals)
# b) accept a list (or any iterable, really)
def __init__(self, vals):
self.point = list(vals)
self.dimensions = len(vals)
I want to define a class Foo whose objects can be used like, foo[1, a=2].
I tried to achieve this by decorating the __getitem__ method of
Foo but with no success. Below is the example code.
def decorator(func):
def func_(*args, **kewargs):
if 'a' in kewargs:
args = list(args) + [kewargs['a']]
return func(*args)
else:
return func(*args)
return func_
class Foo(object):
#decorator
def __getitem__(self, *items):
return items
foo = Foo()
>>> foo.__getitem__(2, a=10)
(2, 10)
>>> foo[2, a=10]
SyntaxError: invalid syntax
So foo[...] is not equivalent to foo.__getitem__(...), something
behind the scene is done for the former. My question is what exactly and how
can I make foo[2, a=10] to work, if at all.
Python allows implicit tuple creation (without parentheses):
In [2]: tup = 1, 2, 3
In [3]: tup
Out[3]: (1, 2, 3)
And it works the same inside square brackets:
In [4]: d = {(1, 2, 3): 4}
In [5]: d[1, 2, 3]
Out[5]: 4
But (2, a=10) is not a valid tuple literal:
In [6]: (2, a=10)
File "<ipython-input-1-7dc03602f595>", line 1
(2, a=10)
^
SyntaxError: invalid syntax
Simply put, you can't make foo[2, a=10] to work, because it's a syntax error no matter how you tweak your __getitem__ implementation.
I'd probably define an ordinary method e.g. get and use it like Foo.get(2, a=10).
This is proposed for python 3.6
Using keyword arguments for indexing is currently a syntax error.
However, PEP472 (https://www.python.org/dev/peps/pep-0472/) proposes this addition to the python syntax.
Workarounds
The PEP also shows workarounds that are currently valid:
foo[2, "a":10] or foo[2, {"a":10}]
I am migrating some of my code from MATLAB. I was wondering if a functionality exists where I define a certain class (3d vector) and I could define arrays (or lists?) of this class. I would like to be able to use slicing operations on this array.
For example, MATLAB has this functionality:
obj = class(s,'class_name')
creates an array of class class_name objects using the struct s as a pattern to determine the size of obj.
I understand that numpy offers everything I need for array operations. I am trying to learn right now and this is just and example. So, I would like to do this without numpy arrays.
I might be completely wrong in approaching it this way, so please feel free to suggest if there are any better methods out there to do this. I was looking into subclassing ndarray, but that seems like I would just be creating an array again. Any suggestions are greatly appreciated.
My code so far:
class vector3d(object):
def __init__(self,*args):
nargs = len(args);
if(nargs == 0): # Null Vector
self.x = None; self.y = None; self.z = None;
elif(nargs==1):
if (type(args[0]) is vector3d):
self = args[0];
elif(type(args[0]) is np.ndarray):
Vec = args[0];
if (np.shape(Vec)[0]==1 or np.shape(Vec)[1]==1):
if (np.shape(Vec) == (3,1)):
self.x = Vec[0,0]; self.y = Vec[1,0];
self.z = Vec[2,0];
elif (np.shape(Vec) == (1,3)):
self.x = Vec[0,0]; self.y = Vec[0,1];
self.z = Vec[0,2];
else:
raise Exception('Wrong Type of Inputs');
else:
raise Exception('Wrong Type of Inputs');
VecArray = np.ndarray((10,), dtype=np.object);
print np.shape(VecArray);
for i in range(10):
print i;
VecArray[i] = vector3d(np.random.rand(3,1));
After running the code, when I try the following:
>>> VecArray[1].x
>>> 0.36923808713820772
>>> VecArray[1:5].x
AttributeError Traceback (most recent call last)
<ipython-input-92-899463ad0461> in <module>()
----> 1 VecArray[1:5].x
AttributeError: 'numpy.ndarray' object has no attribute 'x'
I understand that I could make lists of the object. I should have been more specific. I would like to get an indexable variable as output. For example, something that does not give the above as error.
You can use numpy datatypes:
http://docs.scipy.org/doc/numpy/reference/arrays.dtypes.html
>>> dt = np.dtype([('x', np.int32), ('y', np.int32), ('z', np.int32)])
>>> x = np.array([(1, 2, 3), (3, 2, 1)], dtype = dt)
>>> print x
[(1, 2, 3) (3, 2, 1)]
>>> print x['x'], x['y'], x['z']
[1 3] [2 2] [3 1]
>>> print x[0]['x']
1
Extending example to add some numpy/matlab indexing:
>>> x = np.array([(1, 2, 3), (4, 5, 6), (7, 8, 9)], dtype = dt)
>>> print x[1:]['x']
[4 7]
You can notice that it omits the first element in the X axis (the 1)
EDIT to add some information about how to subclass using custom data type. Using the example in answer from a similar question https://stackoverflow.com/a/5154869/764322 and sightly modifying it:
>>> class Data(np.ndarray):
def __new__(cls, inputarr):
dt = np.dtype([('x', np.int32), ('y', np.int32), ('z', np.int32)])
obj = np.asarray(inputarr, dtype = dt).view(cls)
return obj
def remove_some(self, col, val):
return self[self[col] != val]
>>> a = Data([(1,2,3), (4,5,6), (7,8,9)])
>>> print a
[(1, 2, 3) (4, 5, 6) (7, 8, 9)]
>>> print a.remove_some('x', 4)
[(1, 2, 3) (7, 8, 9)]
I think what you want is numpy.empty:
>>> import numpy as np
>>> a = np.empty((2, 2), dtype=np.object_)
>>> a
array([[None, None],
[None, None]], dtype=object)
This creates an empty array with the specified shape (in this case 2x2) and dtype (in this case, generic objects).
I was asked this question a moment ago and couldn't think of a pythonic solution so I thought I'd throw it out here for a better viewpoint.
What is the best way to extend a base class variable tuple/list etc in a super class?
The following works...
class BaseClass(object):
some_class_member = (1, 2, 3, 4, 5,)
class AnotherClass(BaseClass):
some_class_member = BaseClass.some_class_member + (6, 7,)
a = BaseClass()
a.some_class_member # (1, 2, 3, 4, 5)
b = AnotherClass()
b.some_class_member # (1, 2, 3, 4, 5, 6, 7)
But doesn't seem very pythonic as you have to reference the BaseClass by name so would have to update it if the name changed. Is there a better way to go about this?
I can kind of see your point, however, you're already referencing the BaseClass by name due to specifying it as a parent of your new subclass.
In the interest of the "what if the name changes" scenario, you could provide another layer of inheritance:
class MyBaseClass(BaseClass):
""" This is here so that all of my classes just inherits from this one """
class AnotherClass(MyBaseClass):
super_special_member = MyBaseClass.super_special_member + (6, 7, )
class ClassThatDoesntNeedSuperSpecialMember(MyBaseClass):
""" Cool. """
MyBaseClass would act as a sort of "constant" variable in that you can change what it inherits from and everything else will automatically update.
BaseClass in BaseClass.some_class_member does not mean super of AnotherClass. It just
BaseClass.some_class_member. You can access without instantiation.
>>> BaseClass.some_class_member
(1, 2, 3, 4, 5)
If your purpose is to access the value without instantiation, the simple answer is no.
However, to access the value after instantiation.
How about?
class AnotherClass(BaseClass):
def __init__(self):
self.some_class_member += (6, 7,)
I wonder, is there a function in python -let's call it now apply- that does the following:
apply(f_1, 1) = f_1(1)
apply(f_2, (1, 2)) = f_1(1, 2)
...
apply(f_n, (1, 2,..., n)) = f_n(1, 2,..., n) # works with a tuple of proper length
Since it does exist in eg. A+ and Mathematica and it used to be really useful for me.
Cheers!
Python has language-level features for this, known as "argument unpacking", or just "splat".
# With positional arguments
args = (1, 2, 3)
f_1(*args)
# With keyword arguments
kwargs = {'first': 1, 'second': 2}
f_2(**kwargs)
You can use the * operator for the same effect:
f_1(*(1, 2)) = f_1(1, 2)
...
The expression following the * needn't be a tuple, it can be any expression that evaluates to a sequence.
Python also has a built-in apply function that does what you'd expect, but it's been obsolete in favor of the * operator since Python 2.3. If you need apply for some reason and want to avoid the taint of deprecation, it is trivial to implement one:
def my_apply(f, args):
return f(*args)
Yep, use the * operator on the list of arguments. For a practical example:
max(1, 2, 3, 4, 5) # normal invocation
=> 5
max(*[1, 2, 3, 4, 5]) # apply-like invocation
=> 5
Think of the second snippet as equivalent to apply(max, [1, 2, 3, 4, 5])