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.
 
  • Locked thread
Haystack
Jan 23, 2005





KICK BAMA KICK, I think that you should be able to do what you want to do using SQLalchemy's events. The following (untested, incomplete, possibly fundamentally unsound) modification to your example code might do it.

Python code:
from sqlalchemy import event
from sqlalchemy.orm import backref

class Employee(SQLADeclarativeBase):
    # Other example code elided
    roles = {}

@event.listen_for(Employee, "mapper_configured")
def setup_role_dict(mapper, EmployeeClass):
    #Maps the role backrefs into Employee.roles dict
    for role, relationship in mapper.relationships.items():
        if relationship.info is not None and relationship.info.get("role"):
            EmployeeClass.roles[role] = relationship


class SalesmanRole(SQLADeclarativeBase):
    # also elided
    employee = relationship('Employee', 
        backref=backref("salesman", info={"role":True}) #Note the flag
    )

class CustomerSupportRole(SQLADeclarativeBase):
    # still elided
    employee = relationship('Employee', 
        backref=backref("customer_support", info={"role":True})
    )
The basic idea is that set up an event listener on Employees to wait for all relationships to be configured, which looks for a flag set on the backref relationships you setup on on each of your Role Classes.

I've never actually personally used sqlalchemy's event API, so I have no idea if this will actually work, but it was fun figuring it out :v:

Adbot
ADBOT LOVES YOU

KICK BAMA KICK
Mar 2, 2009

Thanks to both of you; Thermopyle's post introduced me to the term "generic relationship" and I found sqlalchemy-utils, which provides a simple implementation. I can make a collection of those easily enough with an intermediary table but unfortunately generic relationships break automatic cascading, which complicates things.

Haystack's solution seems really close, but there's one snag. A roles dictionary looks like this after the classes are defined:
code:
{'salesman_role': <RelationshipProperty at 0x33c8910; salesman_role>,
'support_role': <RelationshipProperty at 0x33adf50; support_role>}
Its values are RelationshipProperty objects, not the actual targets of the relationship -- like the SalesmanRole instance I've assigned to that Employee. That does get assigned to the Employee.salesman_role attribute but it's not in the collection for iterating, etc.

Haystack
Jan 23, 2005





Oops. I love SQLA, but man does it ever have a lot of abstraction layers. Try:

Python code:
@event.listen_for(Employee, "mapper_configured")
def setup_role_dict(mapper, EmployeeClass):
    #Maps the role backrefs into Employee.roles dict
    for role, relationship in mapper.relationships.items():
        if relationship.info is not None and relationship.info.get("role"):
            EmployeeClass.roles[role] = relationship.class_attribute

KICK BAMA KICK
Mar 2, 2009

That turns out to be an InstrumentedAttribute object. I scrounged the docs and nothing is jumping out at me. Tried poking around in mapper.attrs but couldn't make sense of that either.

Haystack
Jan 23, 2005





Huh. I know what the problem is but I'm a bit stumped as how to circumvent it. Basically, those InstrumentedAttributes are the proxy objects that sqla uses to do things like making employee.name = "bob" work with all the side-effects that it needs (like knowing what's been changed or not). So it's close, but not quite right.

I'll keep poking at it, but at this point I'd recommend taking your question to the sqla mailing list where actual experts can help you out.

KICK BAMA KICK
Mar 2, 2009

Appreciate all your help; learned a lot even if we didn't solve it. I tried some different ideas in that event handler but I'm not even sure it fires consistently. I think I saw some really odd behavior -- different results based on whether a simple print statement I was using to inspect a variable was commented out, I could swear I even saw different results just running the exact same code consecutively.

KICK BAMA KICK
Mar 2, 2009

Goddamnit that was so simple it's worth a double post. Mapping Class Inheritance; the example is even nearly the exact case I was using as my example.

code:
class Role(DeclarativeBase):
    __tablename__ = 'role'
    id = Column(Integer, primary_key=True)
    role_type = Column(String)
    employee_id = Column(Integer, ForeignKey('employee.id'))
    employee = relationship('Employee', backref='roles')

    __mapper_args__ = {
        'polymorphic_identity': 'employee',
        'polymorphic_on': role_type
    }


class SalesRole(Role):
    __tablename__ = 'sales_role'
    id = Column(Integer, ForeignKey('role.id'), primary_key=True)

    __mapper_args__ = {
        'polymorphic_identity': 'sales_role'
    }
    # Etc.


class CustomerSupportRole(Role):
    __tablename__ = 'support_role'
    id = Column(Integer, ForeignKey('role.id'), primary_key=True)

    __mapper_args__ = {
        'polymorphic_identity': 'support_role'
    }
    # Etc.

Dominoes
Sep 20, 2007

Hey, any pitfalls in using an abbreviation like sf in place of self? Seems like an easy way to improve readability by reducing clutter, and should be obvious to other users reading the code. I'm surprised I haven't read about this.

tef
May 30, 2004

-> some l-system crap ->

Dominoes posted:

Hey, any pitfalls in using an abbreviation like sf in place of self? Seems like an easy way to improve readability by reducing clutter, and should be obvious to other users reading the code. I'm surprised I haven't read about this.

Please don't change a word everyone understands to save a measly two characters.

The reason you haven't read about it that it is a patently bad idea and against the spririt of PEP-8

tef
May 30, 2004

-> some l-system crap ->
If someone did that in my team I would revert it, take them to one side, and ask why they did something in the name of readability and yet broke convention and idiom.

Dominoes
Sep 20, 2007

tef posted:

Please don't change a word everyone understands to save a measly two characters.

The reason you haven't read about it that it is a patently bad idea and against the spririt of PEP-8

two * a_lot of characters.

self cluttering is an issue when using complex expressions/comprehensions, especially with long variable names.

Dominoes fucked around with this message at 22:20 on Nov 17, 2014

evol262
Nov 30, 2010
#!/usr/bin/perl

Dominoes posted:

two * a_lot of characters.

self cluttering is an issue when using complex expressions/comprehensions, especially with long variable names.

Hi, I write Python all day every day.

Nobody cares about sf. If I looked at your code without being familiar with "sf", I'd be looking all over the place to find out where you imported that or where you set it as a global or something else. Everyone knows what "self" means. Explicit is better than implicit. Don't try to golf two characters with 'self' -> 'sf'. pep8 and pyflakes exist for a reason. Your code should cleanly pass linters.

qntm
Jun 17, 2009

Dominoes posted:

two * a_lot of characters.

self cluttering is an issue when using complex expressions/comprehensions, especially with long variable names.

Use simpler expressions. Find the balance point between long and short variable names. Consider using comments?

QuarkJets
Sep 8, 2008

Dominoes posted:

two * a_lot of characters.

self cluttering is an issue when using complex expressions/comprehensions, especially with long variable names.

Split your complex expressions/comprehensions over several lines to improve readability. Dropping two letters from "self" breaks a convention that every Python user uses, and while it's true that you can do it you're not really gaining anything by doing so

pmchem
Jan 22, 2010


evol262 posted:

Hi, I write Python all day every day.

Nobody cares about sf. If I looked at your code without being familiar with "sf", I'd be looking all over the place to find out where you imported that or where you set it as a global or something else. Everyone knows what "self" means. Explicit is better than implicit. Don't try to golf two characters with 'self' -> 'sf'. pep8 and pyflakes exist for a reason. Your code should cleanly pass linters.

Speaking of linters, this code does not pass pylint: http://stackoverflow.com/a/6117124

Python code:
import re

rep = {"condition1": "", "condition2": "text"} # define desired replacements here

# use these three lines to do the replacement
rep = dict((re.escape(k), v) for k, v in rep.iteritems())
pattern = re.compile("|".join(rep.keys()))
text = pattern.sub(lambda m: rep[re.escape(m.group(0))], text)
There's a warning about the use of rep in the lambda expression. But man, it's a concise and well-done multiple search and replace on strings in a file. How would you guys write an alternative that passed pylint and PEP-8?

BigRedDot
Mar 6, 2008

Dominoes posted:

Hey, any pitfalls in using an abbreviation like sf in place of self? Seems like an easy way to improve readability by reducing clutter, and should be obvious to other users reading the code. I'm surprised I haven't read about this.

Don't.

KICK BAMA KICK
Mar 2, 2009

pmchem posted:

Speaking of linters, this code does not pass pylint: http://stackoverflow.com/a/6117124

Python code:
import re

rep = {"condition1": "", "condition2": "text"} # define desired replacements here

# use these three lines to do the replacement
rep = dict((re.escape(k), v) for k, v in rep.iteritems())
pattern = re.compile("|".join(rep.keys()))
text = pattern.sub(lambda m: rep[re.escape(m.group(0))], text)
There's a warning about the use of rep in the lambda expression. But man, it's a concise and well-done multiple search and replace on strings in a file. How would you guys write an alternative that passed pylint and PEP-8?
I'm not sure what specific warning you get from pylint but I wonder if explicitly capturing rep as a parameter of the lambda expression with a default value would silence it -- lambda m, rep=rep: ... .

salisbury shake
Dec 27, 2011

Dominoes posted:

two * a_lot of characters.

self cluttering is an issue when using complex expressions/comprehensions, especially with long variable names.

Make a reference with a nice name to self or the attribute you're trying to access before the expression/comprehension.

salisbury shake fucked around with this message at 03:38 on Nov 18, 2014

evol262
Nov 30, 2010
#!/usr/bin/perl
"self cluttering is a problem in big expressions or comprehensions" is code smells.

Why do you have expressions or comprehensions so complex that you think it would be worthwhile to use a shorter alias for "self"?

Are you trying to write Haskell in Python?

I'm into functional programming and all, but very few parts of your code should need enough class variables or methods to make this a problem. If there's one method ruling it all and doing everything, you should break it into smaller pieces. Maybe in smaller, private methods. Maybe those methods are generators which handle whatever state your expression or comprehension is currently trying to do.

Adding a reference to shorten the way it looks to the programmer is lipstick on a pig. It eliminates clarity and adds complexity for no gain.

Is there an example you can show of code that you think would benefit from renaming self?

Suspicious Dish
Sep 24, 2011

2020 is the year of linux on the desktop, bro
Fun Shoe

pmchem posted:

Speaking of linters, this code does not pass pylint: http://stackoverflow.com/a/6117124

Python code:
import re

rep = {"condition1": "", "condition2": "text"} # define desired replacements here

# use these three lines to do the replacement
rep = dict((re.escape(k), v) for k, v in rep.iteritems())
pattern = re.compile("|".join(rep.keys()))
text = pattern.sub(lambda m: rep[re.escape(m.group(0))], text)
There's a warning about the use of rep in the lambda expression. But man, it's a concise and well-done multiple search and replace on strings in a file. How would you guys write an alternative that passed pylint and PEP-8?

Python code:
text = text.replace("condition1", "")
text = text.replace("condition2", "text")

tef
May 30, 2004

-> some l-system crap ->

Dominoes posted:

two * a_lot of characters.

You have wasted more characters writing these terrible posts than you ever will writing sf instead of self.

quote:

self cluttering is an issue when using complex expressions/comprehensions, especially with long variable names.

Four letters is not a long variable name by any reasonable standard. Grow up.

I can only assume you are so bored at your job that you are inventing problems to solve. self is not a long variable name.


I'm going to assume poe's law because I want to live in a world where no-one is this stupid to suggest it, or so stupid as to defend it.

Thermopyle
Jul 1, 2003

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

lol, give the poor guy a break!

How many posts do we need to tell him this?

Symbolic Butt
Mar 22, 2009

(_!_)
Buglord
I kind of understand Dominoes because writing self over and over is kind of frustrating when you don't need to do that as much in other languages like C++, Java and Ruby.

EAT THE EGGS RICOLA
May 29, 2008

Symbolic Butt posted:

I kind of understand Dominoes because writing self over and over is kind of frustrating when you don't need to do that as much in other languages like C++, Java and Ruby.

How the heck is writing 'sf' over and over less annoying than writing 'self'?

evol262
Nov 30, 2010
#!/usr/bin/perl

Symbolic Butt posted:

I kind of understand Dominoes because writing self over and over is kind of frustrating when you don't need to do that as much in other languages like C++, Java and Ruby.

Yeah, because "this" is way less annoying in Java.

tef
May 30, 2004

-> some l-system crap ->

Symbolic Butt posted:

I kind of understand Dominoes because writing self over and over is kind of frustrating

Should I punch a hole in the window because i'm not sure how to open it? I think it will be simple and effective

fritz
Jul 26, 2003

You know you can save an extra 50% of characters by using "s" instead of "sf".

Symbolic Butt
Mar 22, 2009

(_!_)
Buglord

EAT THE EGGS RICOLA posted:

How the heck is writing 'sf' over and over less annoying than writing 'self'?

It doesn't, idk I just said I kinda get why he's frustrated with self :shrug:

Dominoes
Sep 20, 2007

Holy poo poo!

tef
May 30, 2004

-> some l-system crap ->

Thermopyle posted:

lol, give the poor guy a break!

Maybe I have no patience because it's been seven years in this goddamn thread

quote:

How many posts do we need to tell him this?

One didn't seem to be enough.

Symbolic Butt posted:

I kind of understand Dominoes because writing self over and over is kind of frustrating when you don't need to do that as much in other languages like C++, Java and Ruby.

When I was a younger, petulant mess of a programmer, I too waltzed into python and the explicit self. I'd done Java, JavaScript, and a handful of terrible languages not even worth mentioning. I didn't like it much, but I'd often used /this.foo/ instead of /foo/ in Java. I liked to be able to see what I was doing. I wasn't so keen on what seemed to be self sneaking into my method arguments. Now I've been using python for a bit, it's probably the best feature of python.

Let's take a step back and look why it's there and what it does for us: Python doesn't have methods: It has functions in classes. This means you can call a method explicitly on an object, ClassName.method(object, args). This also means you can pull the function out and assign it to other classes, ClassOne.foo = ClassTwo.foo. It also means that obj.foo() is actually two operations, method = obj.foo; method(). The choice of making methods out functions gives us unique ways to build classes, and also lets us take a method and pass it around as a function.

This is incredibly useful, and it's obvious to see why when we compare it to other languages with objects.

In Ruby, you don't even have functions, there isn't really a notion of them. A top level def foo ... end is actually defining a private method on Object. I'll let that sink in: there are no functions in ruby. defining a top level method is monkey patching object. You also have to clumsily call method = object.method(:name_of_method), method.call(), to get the same results. Ruby ends up with a plethora of things that look like functions but all behave differently. Lambdas, Blocks, Procs, Methods.

In javascript, when do obj.foo(), and you do method = obj.foo; method() they do two entirely different things. JavaScript programmers must manually bind methods to objects if they want to capture them to pass around. Let us not forget var that = this — this isn't like a variable, nor acts like one, it doesn't obey the scoping rules that other languages have.

In python: methods are functions, and when you lookup obj.foo, it partially applies the function, setting the first argument to obj. self is just like any other argument, and can be captured inside nested function and class definitions. In many ways, it's not explicit self, but lexical self. it's just another variable, which is why you can call it anything you like. Python could chose to have both method and function types, and special self keyword, but we'd be doing this = self to lexically bind it, and also fun doing bind and unbind to play about with methods.

It gives you all of this expressive power and how do you thank it? Trying to rename it from something *every* python programmer calls it, and *every python programmer* has a habit of writing it. Why do you claim to do it? Readability? Optimization? Time saving. You have already saved yourself so much time by using lexical self that one or two keystrokes isn't going to make a project late.

Whining about lexical self and arguing to remove or shorten it for the very reasons lexical self exists. Lexical self is the best goddam thing in python, take a seat, shut the gently caress up, and learn why it's there. I have no loving patience for such foolish behaviour.

tef
May 30, 2004

-> some l-system crap ->
The absolute broken nature of ruby's ersatz functions is that *adding methods to Object* is considered routine, and *returning a class from inside a method, which captures method arguments* is considered *wtf*. They are living in a world where mashing everything into one namespace makes sense and Foo = namedtuple('Foo', 'x y') is crazy talk. If you want to go over to the world of implict self you are free to go but don't bring any of those trash ideas into python. We have already suffered enough.

Lamacq
Jun 15, 2001

Breezeblock RIP
Guido talks about the importance of self here: http://neopythonic.blogspot.com/2008/10/why-explicit-self-has-to-stay.html although he doesn't address the convention of why self is called "self".

For me it simply comes down to having respect and consideration for the other programmers that will have to understand my code later (including myself). I'm not much of a philosopher but Kant's categorical imperative made a strong impression on me and I think it applies to programming: let's all write code such that if everyone everywhere wrote code in that style we'd all be able to understand it. Taking it upon yourself to rename "self", for any reason, obviously breaks convention and is planting little time bombs of misunderstanding in your code that will bite you or, worse, someone who has to maintain your code after you're gone.

BeefofAges
Jun 5, 2004

Cry 'Havoc!', and let slip the cows of war.

Most IDEs will put it there for you, anyway.

Dominoes
Sep 20, 2007

Thanks qntm, Quark, salisbury, evol, and Symb for the explanations; Going to keep using self.

tef
May 30, 2004

-> some l-system crap ->
I pray that the next bad idea you have doesn't take five people repeating the same thing to you for you to listen :3:

Shy
Mar 20, 2010

JetBrains recently released free PyCharm Educational Edition, I thought someone may find it interesting.

Blinkz0rz
May 27, 2001

MY CONTEMPT FOR MY OWN EMPLOYEES IS ONLY MATCHED BY MY LOVE FOR TOM BRADY'S SWEATY MAGA BALLS

tef posted:

Maybe I have no patience because it's been seven years in this goddamn thread


One didn't seem to be enough.


When I was a younger, petulant mess of a programmer, I too waltzed into python and the explicit self. I'd done Java, JavaScript, and a handful of terrible languages not even worth mentioning. I didn't like it much, but I'd often used /this.foo/ instead of /foo/ in Java. I liked to be able to see what I was doing. I wasn't so keen on what seemed to be self sneaking into my method arguments. Now I've been using python for a bit, it's probably the best feature of python.

Let's take a step back and look why it's there and what it does for us: Python doesn't have methods: It has functions in classes. This means you can call a method explicitly on an object, ClassName.method(object, args). This also means you can pull the function out and assign it to other classes, ClassOne.foo = ClassTwo.foo. It also means that obj.foo() is actually two operations, method = obj.foo; method(). The choice of making methods out functions gives us unique ways to build classes, and also lets us take a method and pass it around as a function.

This is incredibly useful, and it's obvious to see why when we compare it to other languages with objects.

In Ruby, you don't even have functions, there isn't really a notion of them. A top level def foo ... end is actually defining a private method on Object. I'll let that sink in: there are no functions in ruby. defining a top level method is monkey patching object. You also have to clumsily call method = object.method(:name_of_method), method.call(), to get the same results. Ruby ends up with a plethora of things that look like functions but all behave differently. Lambdas, Blocks, Procs, Methods.

In javascript, when do obj.foo(), and you do method = obj.foo; method() they do two entirely different things. JavaScript programmers must manually bind methods to objects if they want to capture them to pass around. Let us not forget var that = this — this isn't like a variable, nor acts like one, it doesn't obey the scoping rules that other languages have.

In python: methods are functions, and when you lookup obj.foo, it partially applies the function, setting the first argument to obj. self is just like any other argument, and can be captured inside nested function and class definitions. In many ways, it's not explicit self, but lexical self. it's just another variable, which is why you can call it anything you like. Python could chose to have both method and function types, and special self keyword, but we'd be doing this = self to lexically bind it, and also fun doing bind and unbind to play about with methods.

It gives you all of this expressive power and how do you thank it? Trying to rename it from something *every* python programmer calls it, and *every python programmer* has a habit of writing it. Why do you claim to do it? Readability? Optimization? Time saving. You have already saved yourself so much time by using lexical self that one or two keystrokes isn't going to make a project late.

Whining about lexical self and arguing to remove or shorten it for the very reasons lexical self exists. Lexical self is the best goddam thing in python, take a seat, shut the gently caress up, and learn why it's there. I have no loving patience for such foolish behaviour.

tef, you're a treasure

hooah
Feb 6, 2006
WTF?

Shy posted:

JetBrains recently released free PyCharm Educational Edition, I thought someone may find it interesting.

Is there somewhere that compares this and the community edition?

Shy
Mar 20, 2010

hooah posted:

Is there somewhere that compares this and the community edition?

I didn't know there's also a community edition.

http://www.jetbrains.com/pycharm-educational/concepts/ posted:

PyCharm Educational Edition is based on the Community Edition and comprises all of its functionality. Additionally, it installs and detects Python during installation. It has a simpler UI (adjustable in settings) and adds a new "Educational" project type.

Adbot
ADBOT LOVES YOU

Symbolic Butt
Mar 22, 2009

(_!_)
Buglord

Yeah, I think I understand why python is the way it is when it comes to self and the object system (mainly because you've been posting on yospos about this subject for so long). It doesn't bother me too much because I don't like to design code around everything being classes and methods, I like functions better most of time so python suits my needs.

But I do think it's the consequence of a fundamental tradeoff (intentional or not) that made attributes be the kings of python. Consequently if you're a fan of writing methods you'll get a bumpy ride... And I guess it's just not possible to create a perfect balance for both approaches.

  • Locked thread