Register a SA Forums Account here!
JOINING THE SA FORUMS WILL REMOVE THIS BIG AD, THE ANNOYING UNDERLINED ADS, AND STUPID INTERSTITIAL ADS!!!

You can: log in, read the tech support FAQ, or request your lost password. This dumb message (and those ads) will appear on every screen until you register! Get rid of this crap by registering your own SA Forums Account and joining roughly 150,000 Goons, for the one-time price of $9.95! We charge money because it costs us money per month for bills, and since we don't believe in showing ads to our users, we try to make the money back through forum registrations.
 
  • Post
  • Reply
Eela6
May 25, 2007
Shredded Hen
I wrote something pretty fun today. I got sick of writing __repr__ for functions, so I made a decorator that does it for me!

Python code:
def easyrepr(cls):
    """class decorator. tries to create self-documenting __repr__. 
    such thast, where possible for immutable objects with 
    implemented __equals__, 
    eval(repr(x)) == x 
    
    """
    _cls_new = cls.__new__
    def _repr(self):
        return self._easyrepr
    def _new(cls, *args, **kwargs):
        instance = _cls_new(cls)
        argstr = (f'{arg!r}' for arg in args)
        kwargstr = (f'{arg} = {kwargs[arg]!r}' for arg in kwargs)
        args = ', '.join(chain(argstr, kwargstr))
        instance._easyrepr = f'{cls.__name__}({args})'
        instance.__repr__ = _repr
        return instance
    cls.__new__ = _new
    cls.__repr__ = _repr
    return cls


if __name__ == '__main__':
    @easyrepr
    class TestClass():
        def __init__(self, foo, *, bar):
            self.foo, self.bar = foo, bar
        def __eq__(self, other):
            return self.foo, self.bar == other.foo, other.bar
    a = TestClass('foo', bar = 'bar')
    b = TestClass('spam', bar = 'eggs')
    c = TestClass(a, bar = b)
    assert repr(c) == "TestClass(TestClass('foo', bar = 'bar'), bar = TestClass('spam', bar = 'eggs'))"
    assert c == eval(repr(c))

Adbot
ADBOT LOVES YOU

SurgicalOntologist
Jun 17, 2004

Nice! Here's mine:

Python code:
from inspect import signature


def call_repr(name, *args, **kwargs):
    args_repr = ', '.join(repr(arg) for arg in args)
    kwargs_repr = ', '.join('{}={!r}'.format(*kwarg) for kwarg in kwargs.items())
    joiner = ', ' if args_repr and kwargs_repr else ''
    return '{}({}{}{})'.format(name, args_repr, joiner, kwargs_repr)



def repr_as_initialized(cls):
    cls._orig__init__ = cls.__init__
    
    def __init__(self, *args, **kwargs):
        self._bound_args = signature(cls).bind(*args, **kwargs)
        self._orig__init__(*args, **kwargs)
    
    def __repr__(self):
        return call_repr(cls.__name__, *self._bound_args.args, **self._bound_args.kwargs)
    
    cls.__init__ = __init__
    cls.__repr__ = __repr__
    return cls


# tests
@repr_as_initialized
class ReprTester:
    def __init__(self, a, b, c=1):
        pass

assert ReprTester.__name__ == 'ReprTester'
assert repr(ReprTester(1, 2)) == 'ReprTester(1, 2)'
assert repr(ReprTester(1, 2, 3)) == 'ReprTester(1, 2, 3)'
assert repr(ReprTester(1, 2, c=3)) == 'ReprTester(1, 2, c=3)'

Eela6
May 25, 2007
Shredded Hen
That's the way I tried to do it at first, but it wasn't inherited properly through subclassing, which bugged me. You get the name (and signature) of the base decorated class.

E.G:

Python code:
>>>class SubClassReprTester(ReprTester):
    pass
>>>print(SubClassReprTester('foo', 'bar', c=1))
ReprTester('foo', 'bar', c=1)
Obviously you can fix this by decorating the subclass with repr_as_initialized.

Eela6 fucked around with this message at 04:35 on Mar 10, 2017

SurgicalOntologist
Jun 17, 2004

Ah, I see. I tried to fix it by replacing cls with type(self) in __init__ and __repr__, but you can't get it to work with an overridden __init__ without using __new__. Nice solution.

Edit: although, as long as proper subclass hygiene is used and the new init starts with a super().__init__ call, it should work.

SurgicalOntologist fucked around with this message at 04:49 on Mar 10, 2017

Boris Galerkin
Dec 17, 2011

I don't understand why I can't harass people online. Seriously, somebody please explain why I shouldn't be allowed to stalk others on social media!

Dominoes posted:

You're catching the TypeError before it occurs; ie once you've gotten thing.foo, you have no more error checking, and 10 doesn't have any keys.

I figured that might be the reason. I guess the interpreter looks to see if I have a 'thing.foo' and it finds it and returns it, and for all purposes the try part has succeeded (well it technically did succeed)?

Dominoes posted:

@property makes methods look like properties; I think they're for writing Java-style code. I wouldn't use them, since they mask that calculations are being run.

Oh okay. This made me reconsider about using properties to begin with. It just seems a bit weird that Python is "a consenting adults language" where nothing is private and everything can be accessed and used, yet @property exists to seemingly make things read-only.

Dominoes posted:

I'm not sure this is a good idea - you're trying to treat it as two different types of objects based on context. Here's a snippet that does what you ask, although as a function rather than a class.

[snip]

Not sure if this helps, but are you familiar with dict's get method? It'll return a default of your choice if it can't find the key.

Thanks for the effort but that wasn't what I was trying to accomplish. I was thinking more on the lines of having a default answer/response with the option to transparently get a more detailed/specific answer.

Like for example Belgium is also called België, Belgique, or Belgien depending on the language. These are official names that their government recognizes since they speak all of those languages listed, and not "unofficial" names that have been translated into other languages (like London and Londres for Spanish and some other languages).

I don't see any harm or why it's a bad idea to be able to do this:

code:
country = Belgium()

country.name
>> Belgium

country.name['german']
# or country.name('german')
# or country.name.german
>> Belgien
I know I can make "name" a method and my problem would be "solved", but then I'd have to type "country.name()" to get the response "Belgium". Those extra empty parenthesis there are exactly what I'm trying to avoid.

If it's not possible to do this then it's whatever, it's not a big deal. But making name a method isn't the answer I'm looking for.

Boris Galerkin fucked around with this message at 08:38 on Mar 10, 2017

Eela6
May 25, 2007
Shredded Hen
Why not just have two attributes? country.name being the default, and country.name_in_lang being a dictionary that holds the other names? I think you're overthinking this.

breaks
May 12, 2001

Boris Galerkin posted:

Oh okay. This made me reconsider about using properties to begin with. It just seems a bit weird that Python is "a consenting adults language" where nothing is private and everything can be accessed and used, yet @property exists to seemingly make things read-only.

You can make things read/write with properties, like @property, def whatever(self), @whatever.setter, def whatever(self, arg). But it's usually somewhat bad form to write something like that from the start. In most cases you would only do that for something that started out as a simple attribute and later needed to become more complicated without breaking user code (so seeing lots of @properties is often kind of symptom of something not being as well architected as it might have been).

Boris Galerkin posted:

I don't see any harm or why it's a bad idea to be able to do this:

I'm skeptical you can really construct a good argument for why jumping through hoops writing possibly buggy code that's irrelevant to the end functionality to enable this weird polymorphic construction, the semantics of which are basically foreign to python, is a big improvement on just typing ().

But if you have such an argument then lets hear it.

Boris Galerkin
Dec 17, 2011

I don't understand why I can't harass people online. Seriously, somebody please explain why I shouldn't be allowed to stalk others on social media!
Because you write code to do things for you, not the other way around. Here's my lovely jumping through the hoops example:

https://repl.it/GP2U/0

code:

class Belgium(object):

    _names = {'en': 'Belgium',
             'de': 'Belgien',
             'nl': 'Belgie',
             'fr': 'Belgique',
             'default': 'Belgium'}

    def __init__(self):

        class Name(dict):
            def __repr__(self):
                return self['default']

        self.name = Name(self._names)

country = Belgium()

print('My name is {}'.format(country.name))
# >>> My name is Belgium

print('My name is also {}'.format(country.name['de']))
# >>> My name is also Belgien

I can understand if this isn't something that exists in Python. I can understand that this isn't "pythonic." But I want to understand why it's a bad idea to do this? I mean ignore the fact that I defined Name inside the init or whatever. I have no idea what __repr__ does but from a quick search it seems to be the thing that gets called when I just type the name of the class.

Typing "country.name_in_german" sounds like more of a hoop, and honestly making it a method and having to call "country.name()" is just ugly when 99% of the times I just need the default value. Plus the () implies function so isn't it semantically wrong? I'm not computing anything, I'm just getting something that already exists.

e: nevermind this only half works. __repr__ returns an actual Name object so I can't do things like "country.name+country.name['de']". Sad.

Boris Galerkin fucked around with this message at 09:23 on Mar 10, 2017

breaks
May 12, 2001

code:
>>> class Country:
	def __init__(self, names):
		self.names = names
	def name(self, language='en'):
		return self.names[language]
>>> belgium = Country({
    'en': 'Belgium',
    'de': 'Belgien',
    'nl': 'Belgie',
    'fr': 'Belgique'
})
Python 101 (not that this is some ideal production worthy idea or whatever), doesn't do anything weird/dumb, doesn't gently caress with the usual semantics of the language, Belgium is an instance of a Country class rather than the other way around, no separate 'default' entry in the dict that is arbitrarily in, and a duplicate of, English, etc etc

On the other hand, what you want to do is actually easy for trivial usage, so here is a way to implement this very, very, very bad localization mechanism (that probably still has some problems but it doesn't matter because you shouldn't do it anyway):

code:
>>> class PleaseDontDoThis(str):
	ohmygod = {
		'i promise this is bad': 'just because you can',
		'does not mean you should': 'trust me on this'
	}
	def __getitem__(self, thisisliterallysatan):
		return self.ohmygod[thisisliterallysatan]
>>> dontkickpuppies = PleaseDontDoThis('thats really mean')
>>> dontkickpuppies
'thats really mean'
>>> dontkickpuppies['i promise this is bad']
'just because you can'
(I know this is SA but I'm a softie so I've edited the latter bit to be less inflammatory, good luck and keep in mind that most the time, making the code work for you is really about making it easy enough to write and understand that you don't have to work for it.)

breaks fucked around with this message at 11:16 on Mar 10, 2017

Nippashish
Nov 2, 2005

Let me see you dance!

Boris Galerkin posted:

e: nevermind this only half works. __repr__ returns an actual Name object so I can't do things like "country.name+country.name['de']". Sad.

__repr__ only gets called to turn your object into a string, so "country.name+country.name['de']" doesn't call __repr__ at all. This is also the essence of why what you want is a bad idea. What type of thing is "country.name"? It can't be a string, because you can't index a string with ['de'], but it needs to behave like a string almost all the time. This means that it needs to be some kind of nearly-a-string object that behaves like the string "Belgium" except in some very special circumstances, and making an object like that will surprise people. That's what you've built with Name, except __repr__ only covers behaving like a string when you print it, and not when do other string things like concatenating or slicing.

If you want to do something like this in a simple and not-surprising way then use a function. It's not semantically wrong for it to be a function because it's a thing that behaves differently depending on what arguments you pass it. If you're really upset about typing country.name() instead of country.name you can do this:

code:
class Belgium(object):

    _names = {'en': 'Belgium',
             'de': 'Belgien',
             'nl': 'Belgie',
             'fr': 'Belgique',
             'default': 'Belgium'}

    @property
    def name(self):
        return self._names['default']
        
    def localized_name(self, lang):
      return self._names[lang]
        
 country = Belgium()
   country.name
=> 'Belgium'
   country.localized_name('de')
=> 'Belgien'
   country.name + country.localized_name('de')
=> 'BelgiumBelgien'
   

QuarkJets
Sep 8, 2008

Boris Galerkin posted:

Because you write code to do things for you, not the other way around. Here's my lovely jumping through the hoops example:

https://repl.it/GP2U/0

code:
class Belgium(object):

    _names = {'en': 'Belgium',
             'de': 'Belgien',
             'nl': 'Belgie',
             'fr': 'Belgique',
             'default': 'Belgium'}

    def __init__(self):

        class Name(dict):
            def __repr__(self):
                return self['default']

        self.name = Name(self._names)

country = Belgium()

print('My name is {}'.format(country.name))
# >>> My name is Belgium

print('My name is also {}'.format(country.name['de']))
# >>> My name is also Belgien
I can understand if this isn't something that exists in Python. I can understand that this isn't "pythonic." But I want to understand why it's a bad idea to do this? I mean ignore the fact that I defined Name inside the init or whatever. I have no idea what __repr__ does but from a quick search it seems to be the thing that gets called when I just type the name of the class.

Typing "country.name_in_german" sounds like more of a hoop, and honestly making it a method and having to call "country.name()" is just ugly when 99% of the times I just need the default value. Plus the () implies function so isn't it semantically wrong? I'm not computing anything, I'm just getting something that already exists.

e: nevermind this only half works. __repr__ returns an actual Name object so I can't do things like "country.name+country.name['de']". Sad.

What you're asking for is for .name to sometimes be a function call if you provide an argument, or a string if you don't call it like a function. That's only going to make your code obtuse and difficult to understand. The code that breaks wrote in the first part of his post looks great and does what you want, but you have to type () when you want to get the default name, which is fine

Space Kablooey
May 6, 2009


Why don't you just use a localization library like Babel?

Boris Galerkin
Dec 17, 2011

I don't understand why I can't harass people online. Seriously, somebody please explain why I shouldn't be allowed to stalk others on social media!
Because it was just an example.

e: And also because I come from a procedural/imperative programming background (my first language was Fortran back when it was still FORTRAN [77]) and I've never learned anything else other than modern versions of Fortran and MATLAB so honestly I have no clue what the point of objects are or when and why I should use them.

I just think it's pretty neat to be able to do:

code:

new_tensor = old_tensor.rotate()

Instead of

code:

new_tensor = rotate_tensor(old_tensor, rotation_matrix)

But honestly the only reason I think the first one is neat is because it looks better to me. I really wasn't joking when I said I liked "foo.thing" better than "foo.thing()" because it looks better without the parenthesis (but mostly because I don't think it's semantically correct to call a function to return a value that I've already computed).

Boris Galerkin fucked around with this message at 17:49 on Mar 10, 2017

Nippashish
Nov 2, 2005

Let me see you dance!

Boris Galerkin posted:

(but mostly because I don't think it's semantically correct to call a function to return a value that I've already computed).

This is a pretty weird thing to think.

huhu
Feb 24, 2006
:woop: My first Flask application is almost done. Hopefully one last question. I've setup Flask-Admin and it works well by routing all traffic (even non logged in users) from /admin to the admin panel. However, /admin is not a route in my views.py file. I thought I could just do something like:
code:
@app.route('/admin', methods = ['GET','POST'])
@login_required
def admin():
    #admin stuff here
but since Flask-Admin handles this route differently, this doesn't seem to be possible. Flask-admin suggests using Flask-Security to do this. Would this just be the easiest way or is there some easy fix to the method I was thinking above?

Space Kablooey
May 6, 2009



Flask admin views have the is_accessible and inacessible_callback methods to control who it lets access and how it should behave when an unauthorized user tries to access their pages.

For example:
Python code:
from flask_admin.base import BaseView
from flask import current_app

def is_accessible(self):
    return current_user.is_authenticated()


def inaccessible_callback(self, name, **kwargs):
    return current_app.login_manager.unauthorized()

BaseView.is_accessible = is_accessible
BaseView.inaccessible_callback = inaccessible_callback

No Safe Word
Feb 26, 2005

Not to pick at an old scab or anything but in the old thread there was a bit of back-and-forth over _ as a "discard me" variable name and whether or not that was better than something more explicit.

So, although it's not Python, it's worth noting that C# 7 specifically outlines _ as a "discard" variable with their new use-case for out variables (the very end of the "Out Variables" section). I thought that was interesting.

Eela6
May 25, 2007
Shredded Hen

Boris Galerkin posted:

Because it was just an example.

e: And also because I come from a procedural/imperative programming background (my first language was Fortran back when it was still FORTRAN [77]) and I've never learned anything else other than modern versions of Fortran and MATLAB so honestly I have no clue what the point of objects are or when and why I should use them.

I just think it's pretty neat to be able to do:

code:
new_tensor = old_tensor.rotate()
Instead of

code:
new_tensor = rotate_tensor(old_tensor, rotation_matrix)
But honestly the only reason I think the first one is neat is because it looks better to me. I really wasn't joking when I said I liked "foo.thing" better than "foo.thing()" because it looks better without the parenthesis (but mostly because I don't think it's semantically correct to call a function to return a value that I've already computed).

I too came from MATLAB, where you can call a zero-argument function without the parentheses.

The reason why you can't (well, shouldn't) in python is because Python has functions as first class objects. That means you can put functions into containers, have functions with extra attributes (that could be functions themselves), all sorts of weird stuff. This is powerful, but it means you need a way to refer to the function itself as an object.

When you use the parentheses you are referring to the action of the function.

When you don't use the parentheses you refer to the function itself. This is a crucial distinction!

Here's an example that might help.

IN:
Python code:
def print_0():
    print(0)
def print_1():
    print(1)
def print_2():
    print(2)
    
function_list = [print_0, print_1, print_2]

for func in function_list:
    func.extra_attribute = 'hello, I am '+func.__name__
    print(func.extra_attribute)
    print(func)
    func()
OUT
code:
hello, I am print_0
<function print_0 at 0x000001E71F2AB400>
0
hello, I am print_1
<function print_1 at 0x000001E71F2ABE18>
1
hello, I am print_2
<function print_2 at 0x000001E71F2AB378>
2
If you write Python, people have expectations about 'how' your code works. You can break this with weird metaprogramming and operator overloading - Python gives you a lot of power. But this is strongly discouraged! If everyone write Python in the 'normal', idiomatic way, the community is more easily able to use and reason about each others' code.

Other languages do not have this philosophy. I am of the opinion it is one of the best things about Python, but you may disagree. That's OK; just be warned the majority of the community will not be on your side.

The Zen Of Python posted:

Explicit is better than implicit.
Special cases aren't special enough to break the rules.

Eela6 fucked around with this message at 22:00 on Mar 10, 2017

QuarkJets
Sep 8, 2008

Boris Galerkin posted:

Because it was just an example.

e: And also because I come from a procedural/imperative programming background (my first language was Fortran back when it was still FORTRAN [77]) and I've never learned anything else other than modern versions of Fortran and MATLAB so honestly I have no clue what the point of objects are or when and why I should use them.

I just think it's pretty neat to be able to do:

code:
new_tensor = old_tensor.rotate()
Instead of

code:
new_tensor = rotate_tensor(old_tensor, rotation_matrix)
But honestly the only reason I think the first one is neat is because it looks better to me. I really wasn't joking when I said I liked "foo.thing" better than "foo.thing()" because it looks better without the parenthesis (but mostly because I don't think it's semantically correct to call a function to return a value that I've already computed).

Your points on the first codeblock are very good, and indeed that does look better and work better and it makes sense. But then consider this:

code:
new_tensor = old_tensor.rotate
new_tensor2 = old_tensor.rotate(rotation_matrix2)
The above code block is basically what you were asking for before, in that "rotate" maps to two different things (a matrix or a function) depending on usage. That's confusing and not actually any cleaner than:

code:
new_tensor = old_tensor.rotate()
new_tensor2 = old_tensor.rotate(rotation_matrix2)
In this code there's no question over what's going on here; rotate() is clearly a method that returns a new tensor, and it accepts an optional input in the form of a new rotation matrix. This is clean, simple, and easy to understand. The previous code block is ambiguous and definitely not any cleaner or simpler to understand

quote:

(but mostly because I don't think it's semantically correct to call a function to return a value that I've already computed)

That's called a get method, those are common in object-oriented languages but in Python you don't have to use them.

Hammerite
Mar 9, 2007

And you don't remember what I said here, either, but it was pompous and stupid.
Jade Ear Joe

Boris Galerkin posted:

(but mostly because I don't think it's semantically correct to call a function to return a value that I've already computed).

There are plenty of examples of situations where "calling a function to return a value already computed" is normal and accepted*, so I think you're going to have to accept that this viewpoint is highly idiosyncratic. Coming up with an attribute of your object that behaves in some cases like a function and in other cases like the return value of the same function is pretty far from idiomatic, so you're going to have to accept that if you do that, just about anyone who needs to read or understand your code is going to have trouble doing so. That's slightly dependent on the language background of the individual but I think it would apply for most languages in common use. If I see ob.f() in one place and then ob.f somewhere else, I'm naturally going to think that the use of ob.f is to refer to the method itself (as Eela6 says) rather than to invoke it; I'll then be surprised to learn that's not what's happening.

You should not in general see invocation of functions as an expense.

* Aside from concepts like getter methods, consider the case of an object with an attribute that's time-consuming to calculate. In this case, it wouldn't be unusual to cache the latest calculated value for the attribute and also have a flag variable that indicates whether the stored value is stale. You then would add a method to allow the user of the object to fetch the value of the attribute. If the "stale" flag is set then the attribute is computed and then the computed value is stored and returned; if it's not, then the stored value is returned as-is.

Hammerite
Mar 9, 2007

And you don't remember what I said here, either, but it was pompous and stupid.
Jade Ear Joe

Boris Galerkin posted:

so honestly I have no clue what the point of objects are or when and why I should use them.

The point of an object is to package up the knowledge and responsibilities of parts of your program into little packages that are easy for human beings to understand and reason about. If you were programming a first-person shooter game*, you might have a "player" object, "enemy" objects, "projectile" objects and so forth. Then instead of keeping track of enemies' health in some array of health values or something, you can access an individual enemy's health through that enemy object. If the enemy takes damage, you call a method enemy.take_damage(amount), which would take care of things like checking whether the enemy is now dead, whether it should change its behaviour now that it has less health, and so on. A lot of the benefits of this are already realised through the use of functions, which is an even more fundamental way to make programs easier to understand, but there are additional benefits that come from strongly associating data (the enemy's health) with the code that needs to care about it (the method take_damage()). A lot of usages of classes are more abstract than that, but the key idea is always making it easier for people to reason about your code and dividing up the responsibilities of your program.

* not that I would choose to try to do that in Python, mind you.

Thermopyle
Jul 1, 2003

...the stupid are cocksure while the intelligent are full of doubt. —Bertrand Russell

Boris Galerkin posted:

Because it was just an example.

e: And also because I come from a procedural/imperative programming background (my first language was Fortran back when it was still FORTRAN [77]) and I've never learned anything else other than modern versions of Fortran and MATLAB so honestly I have no clue what the point of objects are or when and why I should use them.

I just think it's pretty neat to be able to do:

code:
new_tensor = old_tensor.rotate()
Instead of

code:
new_tensor = rotate_tensor(old_tensor, rotation_matrix)
But honestly the only reason I think the first one is neat is because it looks better to me. I really wasn't joking when I said I liked "foo.thing" better than "foo.thing()" because it looks better without the parenthesis (but mostly because I don't think it's semantically correct to call a function to return a value that I've already computed).

I agree that property accesses are more "pleasant" to type and look at than function calls.

Anyway, do you ever split your code into different files for reasons other than language-mandated reasons? The general reason you'd do that is one of the general reasons you'd use objects.

Of course, OOP is full of stupid ideas as well. It's a good idea to become familiar with different paradigms like OOP, functional, etc so you don't end up just using what you know and missing out on better ways to solve problems.

You talking about your background makes me think you might like learning Elm, as it's completely different from what you'res used to for completely different purposes, and it'll really expand your mind about what you can do with Python as you can implement a lot of the same functional ideas in Python.

Eela6
May 25, 2007
Shredded Hen
Elm is a neat language! I'm not sure if I love it but there are a lot of interesting ideas there.

Seventh Arrow
Jan 26, 2005

I'm an utter neophyte, not just with python, but with programming in general so please bear with me. I'm working through a beginner-oriented book and he's going through lists and sorting. At first he shows how to display a list in reverse order:

code:
cars = ['bmw', 'audi', 'toyota', 'subaru']
cars.sort(reverse=True)
print(cars)
Next, he shows how to show the list in a sorted order without affecting the original order:

code:
cars = ['bmw', 'audi', 'toyota', 'subaru']

print("Here is the original list:")
print(cars)

print("\nHere is the sorted list:")
print(sorted(cars))

print("\nHere is the original list again:")
print(cars)
So finally, the point: he says that the sorted() function can also accept a reverse=True argument to display the list in reverse alphabetical order. He doesn't say how to do this, though, and the following exercise requires it. If I do

code:
cars = ['bmw', 'audi', 'toyota', 'subaru']
print(sorted(reverse=True))
print(cars)
it doesn't like it. None of the variations that I try work either. Any suggestions? I tried googling it but a lot of the results seemed overly complex and didn't really answer the question.

Nippashish
Nov 2, 2005

Let me see you dance!
You need to tell sorted what it's sorting. In your last example, how does it know it's supposed to be sorting the list of cars?

You should probably also get used to looking up the documentation for functions you are using. Even if you don't understand everything its a good habit to get into because because a lot of these types of questions have answers that live in the documentation.

Eela6
May 25, 2007
Shredded Hen
Whenever you call a function, it does some action, then returns an object.

The function sorted() takes an iterable* and returns a sorted list of the contents. This is a new list; not the old one.

list.sort(), on the other hand, is a method of a list (a special kind of function, attached to an object), that sorts it without making a new copy. To remind programmers of the difference, it returns None.

Try this
code:

a_list = [3, 1, 2]
b_list = sorted(a_list)
print(a_list)
print(b_list)
print(a_list.sort())
print(a_list)
a_list.append(4)
print(a_list)
print(b_list)
What are the results? Are they what you expected?

*An iterable is an abstraction of anything that can give you a 'flow' of objects, one at a time. Tuples, lists, sets, and dictionaries are all iterable. For now, all you need to know is anything that supports
code:
for x in iterable:
    #code
is iterable.

baka kaba
Jul 19, 2003

PLEASE ASK ME, THE SELF-PROFESSED NO #1 PAUL CATTERMOLE FAN IN THE SOMETHING AWFUL S-CLUB 7 MEGATHREAD, TO NAME A SINGLE SONG BY HIS EXCELLENT NU-METAL SIDE PROJECT, SKUA, AND IF I CAN'T PLEASE TELL ME TO
EAT SHIT

There's two subtly different things going on there that you probably haven't noticed

Different types of object have different functions you can invoke on an instance of that object, called methods. It's like a button you can press on an object you created. So in your first example, you create a list object and then call that list's sort() method by using the dot notation - cars.sort(). You're explicitly acting on the cars list by pushing its button

In your second version you're using a standalone function called sorted(), which works sort of like a service - it does something useful, but you have to give it the object it's going to work with. Which makes sense - what are you sorting? You need to provide a list object as a parameter so it can do its thing, like sorted(cars). Your broken version didn't do that, so you weren't actually telling it what to sort

Without complicating stuff talking about self, those are the two different ways you'll work with objects - either calling methods on the object or passing them into a function, so get used to seeing it!

Knowing that lists have a sort() method that reorders your list elements in-place, or that the sorted() function takes a list and spits out a new list without changing the original one, those require familiarity by reading the documentation. So like someone said, get used to looking stuff up to see how it works - it'll also tell you what the required and optional parameters are so you can see what's possible. Definitely get familiar with Python's basic types and all the stuff you can do with them, refer to the docs often!

Here's a bunch of built-in functions - these are the things where you need to pass in any objects the function will be working with

e- beaten by my own lost post bug :negative:

baka kaba fucked around with this message at 04:58 on Mar 12, 2017

curufinor
Apr 4, 2016

by Smythe
I was at an interview and one guy kept grilling on me for saying that I would use celery as a backend for a restful flask service that needed queueing bits, because celery drops tasks stochastically. Any ideas where he got that from, or any links like that? A cursory google doesn't help

I found a 2-year-old HN dealie with the comments stomping on the project generally, are those complaints still true nowadays?

edit: lol more stomping 3 years ago lols

curufinor fucked around with this message at 06:09 on Mar 12, 2017

Seventh Arrow
Jan 26, 2005

Thanks very much, guys! This is good stuff to work with.

Boris Galerkin
Dec 17, 2011

I don't understand why I can't harass people online. Seriously, somebody please explain why I shouldn't be allowed to stalk others on social media!

QuarkJets posted:

Your points on the first codeblock are very good, and indeed that does look better and work better and it makes sense. But then consider this:

code:
new_tensor = old_tensor.rotate
new_tensor2 = old_tensor.rotate(rotation_matrix2)

For reasons that I can't seem to articulate, this example doesn't make sense to me as something I would do. In my mind it's pretty clear that "rotate" is a function that does the rotation. It's a function, so I would be 100% fine with calling it like a function, eg old_tensor.rotate().

code:
# Call old_tensor to rotate itself against a rotation matrix and return the rotated tensor.
new_tensor = old_tensor.rotate()

# Call old_tensor to retrieve an already rotated tensor and return it.
# ... Maybe I pre-computed the rotated tensor in __init__ for better memory management.
# ... Maybe when I called the rotate() method it also saved a copy of it for retrieval later.
new_tensor2 = old_tensor.rotated
I appreciate all the help guys. It looks like my way of thinking about things might be wrong. If anything the insight is really helpful to me and I'm learning about things outside of my familiar box so that's not a bad thing at least.

Thermopyle posted:

Anyway, do you ever split your code into different files for reasons other than language-mandated reasons? The general reason you'd do that is one of the general reasons you'd use objects.

Yep of course. I split my code/functions into separate files/modules in a way that makes sense (e.g., functions that act on tensors go in tensors.py). I'm just having a hard time seeing the practical difference between these two though:

code:
def rotate_tensor(tensor, rotation_matrix):
    A = tensor
    Q = rotation_matrix
    return Q*A*transpose(Q)

new_tensor = rotate_tensor(old_tensor, some_matrix)
vs

code:
class Tensor(object):

    def __init__(self, tensor, rotation_matrix):
    self.A = tensor
    self.Q = rotation_matrix

    def rotate(self, rotation_matrix=None):
    Q = rotation_matrix or self.Q
    return Q*self.A*transpose(Q)

old_tensor = Tensor(a_numpy_ndarray, rotmat)
new_tensor = old_tensor.rotate()
They both do the same thing. It's just more elegant looking to be able to call a rotate() method I think, but at the end of the day all I really care about is that the rotate method or rotate_tensor function gives me back a properly rotated tensor. Plus it's neat that I can assign the rotation matrix to the object already and not have to worry about passing the rotation matrix around anymore. That's the really cool part for me.

quote:

You talking about your background makes me think you might like learning Elm, as it's completely different from what you'res used to for completely different purposes, and it'll really expand your mind about what you can do with Python as you can implement a lot of the same functional ideas in Python.

Neat, I'll check it out.

e:

quote:

Of course, OOP is full of stupid ideas as well. It's a good idea to become familiar with different paradigms like OOP, functional, etc so you don't end up just using what you know and missing out on better ways to solve problems.

Yep this is what I'm trying to do as well. I'm not making any claims about one way being better or whatever. I'm just drawing from experience: this is how I learned to do things, this is how I've always done things. I had to pick up Python to work on a project, and I'm honestly trying my best to learn how to do things "properly" with Python instead of trying to shoehorn in how I'd do things with C. So basically all of my questions are probably a mixture of "how do I do this in Python" and "why do I need to do it like this in OOP". I try to keep the OOP specific things out of here though.

Boris Galerkin fucked around with this message at 12:37 on Mar 12, 2017

QuarkJets
Sep 8, 2008

Boris Galerkin posted:

code:
def rotate_tensor(tensor, rotation_matrix):
    A = tensor
    Q = rotation_matrix
    return Q*A*transpose(Q)

new_tensor = rotate_tensor(old_tensor, some_matrix)
vs

code:
class Tensor(object):

    def __init__(self, tensor, rotation_matrix):
    self.A = tensor
    self.Q = rotation_matrix

    def rotate(self, rotation_matrix=None):
    Q = rotation_matrix or self.Q
    return Q*self.A*transpose(Q)

old_tensor = Tensor(a_numpy_ndarray, rotmat)
new_tensor = old_tensor.rotate()
They both do the same thing. It's just more elegant looking to be able to call a rotate() method I think, but at the end of the day all I really care about is that the rotate method or rotate_tensor function gives me back a properly rotated tensor. Plus it's neat that I can assign the rotation matrix to the object already and not have to worry about passing the rotation matrix around anymore. That's the really cool part for me.

Yup, they both do the same thing and the decision to use one implementation vs the other is going to come down to some combination of personal preference, conforming to some preexisting style guide, and specific project details (for instance if you're writing high-performance computing code then a functional form is often better, since it's easier to port that to C/Fortran or to compile it with Numba, but if you're interacting with Java for some god-awful reason then Java is classes-only)

baka kaba
Jul 19, 2003

PLEASE ASK ME, THE SELF-PROFESSED NO #1 PAUL CATTERMOLE FAN IN THE SOMETHING AWFUL S-CLUB 7 MEGATHREAD, TO NAME A SINGLE SONG BY HIS EXCELLENT NU-METAL SIDE PROJECT, SKUA, AND IF I CAN'T PLEASE TELL ME TO
EAT SHIT

Boris Galerkin posted:

For reasons that I can't seem to articulate, this example doesn't make sense to me as something I would do. In my mind it's pretty clear that "rotate" is a function that does the rotation. It's a function, so I would be 100% fine with calling it like a function, eg old_tensor.rotate().

code:
# Call old_tensor to rotate itself against a rotation matrix and return the rotated tensor.
new_tensor = old_tensor.rotate()

# Call old_tensor to retrieve an already rotated tensor and return it.
# ... Maybe I pre-computed the rotated tensor in __init__ for better memory management.
# ... Maybe when I called the rotate() method it also saved a copy of it for retrieval later.
new_tensor2 = old_tensor.rotated

I might be missing something, but are these meant to return the same value? Or do multiple calls to rotate() stack additional rotations, and the .rotated property just returns the last calculated one (meaning you're keeping internal state about how many rotations have been applied)?

If you're not maintaining state, and you just want that extra property to avoid recalculating the rotated version of the tensor every time you want to access it, you can just have one method and lazily calculate it - have a result variable (initially set to None), and when you call rotate(), check if the result is empty and calculate it if necessary, then return it.

Just a single method, no need to worry about whether it's already been called or not, and you can change the implementation in the class (like calculating rotations in init or allowing optional rotation matrix parameters in the rotate() call, with multiple cached values) without the callers needing to care. Newer pythons give you a free LRU cache decorator if you want instant no-worries memoization of results. This is one of the things OOP gives you, you can encapsulate state and behaviour and wall it off behind the interface - so long as the interface stays consistent, nothing outside of the class needs to know anything about how it works. You can treat it as a black box

Thermopyle
Jul 1, 2003

...the stupid are cocksure while the intelligent are full of doubt. —Bertrand Russell

Boris Galerkin posted:

Yep of course. I split my code/functions into separate files/modules in a way that makes sense (e.g., functions that act on tensors go in tensors.py). I'm just having a hard time seeing the practical difference between these two though:

code:
def rotate_tensor(tensor, rotation_matrix):
    A = tensor
    Q = rotation_matrix
    return Q*A*transpose(Q)

new_tensor = rotate_tensor(old_tensor, some_matrix)
vs

code:
class Tensor(object):

    def __init__(self, tensor, rotation_matrix):
    self.A = tensor
    self.Q = rotation_matrix

    def rotate(self, rotation_matrix=None):
    Q = rotation_matrix or self.Q
    return Q*self.A*transpose(Q)

old_tensor = Tensor(a_numpy_ndarray, rotmat)
new_tensor = old_tensor.rotate()
They both do the same thing. It's just more elegant looking to be able to call a rotate() method I think, but at the end of the day all I really care about is that the rotate method or rotate_tensor function gives me back a properly rotated tensor. Plus it's neat that I can assign the rotation matrix to the object already and not have to worry about passing the rotation matrix around anymore. That's the really cool part for me.
Yeah, they do the same thing, and I'd say someone was drinking the OOP koolaid if they implemented the OOP version in isolation.

You'll often see classes used in python to lump logically-related code together, so in just that sense you can see them as another unit of subdivision. You have packages (directories with a __init__.py) file, modules (.py files), and classes, all that help you organize code.

That being said, classes are more than just a way to group logically-related code together.

It's hard to describe a slam-dunk case for objects because everything you can do with objects you can do with functions. Objects are for helping you reason about your code, they don't make your code able to do new things.

I think the best you can do is understand how objects work and be aware of them. Try to use them even when you don't think you need them. It'll help you get a feel for them so that when you do encounter problems that would benefit from an OOP approach, you'll know to use them.

There's a not-entirely-unjustified school of thought in python-land that too many developers pull out classes unnecessarily.

Eela6
May 25, 2007
Shredded Hen

Thermopyle posted:

There's a not-entirely-unjustified school of thought in python-land that too many developers pull out classes unnecessarily.

I am of this opinion.

This is a good video on the subject:
[video type=""]https://m.youtube.com/watch?v=o9pEzgHorH0[/video]

Classes are useful when they make your code easier to reason about. Too many classes are often a signal of code without forethought.

dougdrums
Feb 25, 2005
CLIENT REQUESTED ELECTRONIC FUNDING RECEIPT (FUNDS NOW)
I tried writing a hella OOP version of a thing in python like I might in javaish C#, and it took me far too long to realize that I was typing out the same stuff over and over again when none of it is enforced or anything; might as well just use them for the constructors. Python's OOP seems more useful when you do something like rust's traits with it.

Dominoes
Sep 20, 2007

Objects are nice if you're defining something that can be thought of as a datatype, or if you're using a collection of similar items.

huhu
Feb 24, 2006
This code
code:
import sys

#TB I need to setup virtual env

activate_this = '/home/huhu/.virtualenvs/wv_venv/bin/activate_this.py'
exec(activate_this, dict(__file__=activate_this))

sys.path.append('/home/tbumgarner/webapps/chs_web_viewer/web_viewer')
import app as application
is leading to this error. I for the life of me have no idea why this is happening.
code:
[Sun Mar 12 20:10:50.924896 2017] [wsgi:error] [pid 7297:tid 139694796572416] [remote 127.0.0.1:50826]

     exec(activate_this, dict(__file__=activate_this))
   File "<string>", line 1
     /home/huhu/.virtualenvs/wv_venv/bin/activat$
     ^
 SyntaxError: invalid syntax
I was following this tutorial and had to change the execfile() command for python3.

Alexei
Aug 16, 2007

huhu posted:

This code
code:
import sys

#TB I need to setup virtual env

activate_this = '/home/huhu/.virtualenvs/wv_venv/bin/activate_this.py'
exec(activate_this, dict(__file__=activate_this))

sys.path.append('/home/tbumgarner/webapps/chs_web_viewer/web_viewer')
import app as application
is leading to this error. I for the life of me have no idea why this is happening.
code:
[Sun Mar 12 20:10:50.924896 2017] [wsgi:error] [pid 7297:tid 139694796572416] [remote 127.0.0.1:50826]

     exec(activate_this, dict(__file__=activate_this))
   File "<string>", line 1
     /home/huhu/.virtualenvs/wv_venv/bin/activat$
     ^
 SyntaxError: invalid syntax
I was following this tutorial and had to change the execfile() command for python3.

Check the documentation for python 3's exec(): https://docs.python.org/3/library/functions.html#exec

"This function supports dynamic execution of Python code. object must be either a string or a code object. If it is a string, the string is parsed as a suite of Python statements which is then executed (unless a syntax error occurs)."

So the problem is that you're passing the string "/home/huhu/.virtualenvs/wv_venv/bin/activate_this.py'" to the exec() function, which is just a path and not really python code, hence the syntax error.

VikingofRock
Aug 24, 2008




Dominoes posted:

Objects are nice if you're defining something that can be thought of as a datatype, or if you're using a collection of similar items.

I prefer to use namedtuples for this personally.

E: which, I suppose, is actually a class, but people tend to treat them differently in their mental model of how things work.

Adbot
ADBOT LOVES YOU

Dominoes
Sep 20, 2007

namedtuple unless it's mutable, or would benefit from methods.

  • 1
  • 2
  • 3
  • 4
  • 5
  • Post
  • Reply