|
|
|
|
[herpderp p0tdroid macht faxen]
|
[Dieser Beitrag wurde 1 mal editiert; zum letzten Mal von flying sheep am 23.02.2013 20:24]
|
|
|
|
|
|
Is auch oft genug kritisert worden, dass die PHP-Entwickler selber offensichtlich nicht wirklich wissen, was Objekte, Hashmaps, assoz. Container, Listen und Arrays sind. (Wie war das noch gleich, man kann über ein einzelnes Objekt iterieren, dann zerlegt man das nämlich als wäre es ein assoz. Container. Wat? )
|
|
|
|
|
|
|
|
|
|
|
| Zitat von csde_rats
Is auch oft genug kritisert worden, dass die PHP-Entwickler selber offensichtlich nicht wirklich wissen, was Objekte, Hashmaps, assoz. Container, Listen und Arrays sind. (Wie war das noch gleich, man kann über ein einzelnes Objekt iterieren, dann zerlegt man das nämlich als wäre es ein assoz. Container. Wat? )
| |
Das Array ist sogesehen der einzige in PHP implementierte Container. Theoretisch kannst du mit dem fast alles machen. Entsprechend sind Objekte auch ein Teil dieses Containers.
Im Link oben beschreibt das jemand auch ganz schön mit "concepts merged into superconcepts".
|
[Dieser Beitrag wurde 1 mal editiert; zum letzten Mal von Daddi89 am 23.02.2013 20:05]
|
|
|
|
|
|
Einer für alles und alle für einen ist aber eher weniger tolles Softwaredesign. Und PHP ist schließlich nicht nur Software, sondern gar Sprache, wo herausragendes Design viel viel wichtiger ist, weil schlechtes Sprachdesign direkt in die Software durchschlägt.
|
|
|
|
|
|
|
| Zitat von Ashtray
Nie wieder Alkohol
| |
süß
|
|
|
|
|
|
|
| Git Extensions is the only graphical user interface for Git that allows you control Git without using the commandline. | | mein kde hält das ja für stark übertrieben:
|
|
|
|
|
|
|
| Zitat von csde_rats
Is auch oft genug kritisert worden, dass die PHP-Entwickler selber offensichtlich nicht wirklich wissen, was Objekte, Hashmaps, assoz. Container, Listen und Arrays sind. (Wie war das noch gleich, man kann über ein einzelnes Objekt iterieren, dann zerlegt man das nämlich als wäre es ein assoz. Container. Wat? )
| |
an den kommentaren zu dem link sieht man auch wieder ganz deutlich die apologeten, die nichts auf „ihre“ sprache kommen lassen und absolut nicht in der lage sind oder sein wollen, auch nur zu versuchen, den sachverhalt und die kritik zu verstehen. es geht ja nur um den namen, und darum, dass dieser einen falschen eindruck erweckt, aber nein, nichtmal das lassen die als kritik zu.
PHP: „nennen wir’s array, das kennen die leute und man kann es ja auch als eines nutzen, solange man nix rauslöscht und es in der reihenfolge der keys initialisiert“
Lua: „nennen wir’s table, dann checken die leute, dass es kein array ist.“
gefährliches design vs. vernünftiges design, ganz simpel.
|
|
|
|
|
|
|
hmm, klar ist das introspection. und es ist ein ducktyping weil du ein „settings“-attribut erwartest
du hast auch ein paar räder neu erfunden:
- dein trim existiert als textwrap.dedent
-
{module}.{class}.format … gibt es als clas.__qualname__
- wenn du
Setting.format einfachSetting.__str__ nennen würdest, köntest duin format_settings statt:
return "\n".join(map(lambda setting: setting.format(), self.settings))
das hier tun:
return "\n".join(map(str, self.settings))
aber in jedem fall ginge auch
return "\n".join(setting.format() for setting in self.settings)
|
|
|
|
|
|
|
Ah, wenn man nur nach "docstring trim" googelt findet man das nicht. Wunderbar, hat mich irgendwie gewundert, dass das nicht in der Stdlib drin sein soll.
__qualname__ ist afaik Py3.3, aktuell würde ich noch gern 3.2-compat haben (weil einfach viele Distros noch kein 3.3 haben)
__str__… total vergessen
(fe: SettingsBase definiert settings und required als leeres Tupel)
|
|
|
|
|
|
|
mal schauen, wer am Montag alles zur Arbeit kommt... der direkte Nachbar ist schon seit di krankgeschrieben und am Freitag sind 3 früher nach Hause wells ihnen nicht gut ging... und ich werd natürlich wieder am Wochenende krank und am Montag gehts bestimmt wieder :rolleyes:
|
|
|
|
|
|
|
| Zitat von csde_rats
Ah, wenn man nur nach "docstring trim" googelt findet man das nicht. Wunderbar, hat mich irgendwie gewundert, dass das nicht in der Stdlib drin sein soll.
__qualname__ ist afaik Py3.3, aktuell würde ich noch gern 3.2-compat haben (weil einfach viele Distros noch kein 3.3 haben)
__str__… total vergessen
(fe: SettingsBase definiert settings und required als leeres Tupel)
| | ich bin auch doof, __qualname__ enthält das module gar nicht. das ist für innere klassen und methoden und so.
und vergiss das mit dem generator-in-funktionsaufrufen nicht, das ist cool
|
|
|
|
|
|
|
Das is son richtig schickes Stück Syntaxzucker.
Allgemein bin ich inzwischen ganz zufrieden wie ich mir das so zusammengehackt habe. Ich fand es ursprünglich z.b. sehr verbose wie die Konfiguration war, das sah z.B. so aus:
from i3pystatus import (… modsde …
mdesettings = {
username
password
…
}
status.register(modsde.ModsDeChecker(mdesettings))
Mit etwas rumgehacke kann man das zum einen auf verschiedene Arten schreiben (das oben geht so auch noch) [ich mag nicht immer die config-files der anderen leute zerbrechen ]
, aber auch so:
from … import…
status.register(modsde, username=…, password=…
oder so
status.register(modsde, {"username": …, "password":…})
Das fliegt genau dann in die Luft, wenn im Modul modsde plötzlich noch ein anderes i3pystatus.Module auftaucht. Dann gibt's nen ConfigAmbigiousClassesError…
Das angenehme daran is auch, dass das ganze Setting-gehandle komplett aus den Modulen raus is. Die Module sagen an, was es so gibt, das sieht z.B. so aus:
class ModsDeChecker(IntervalModule):
"""
This class returns i3status parsable output of the number of
unread posts in any bookmark in the mods.de forums.
"""
settings = (
("format", """Use {unread} as the formatter for number of unread posts"""),
("offset", """subtract number of posts before output"""),
"color", "username", "password"
)
required = ("username", "password")
color = "#7181fe"
offset = 0
format = "{unread} new posts in bookmarks"
Das settings Tupel kann Strings und Paare drin sein. Standardwerte setzt man einfach in der Klasse und required sagt an, was gesetzt sein muss.
Der Konstruktor von SettingsBase (eine Basis von Module eine Basis von AsyncModule eine Basis von IntervalModule) wurschtelt sich dann da durch und versucht soweit möglich einfach das richtige zu tun.
Naaachteil: Das hat mich bestimmt 200 SLOC gekostet, wo ich doch ursprünglich das Ziel hatte weniger Code zu haben. Aber naja. Man kann nich alles haben.
|
|
|
|
|
|
|
Vorteil is halt viel schönere Configfiles und die Dokumentation is durchgehend im Quelltext. die README.md wird von i3pystatus.mkdocs erzeugt…
|
|
|
|
|
|
|
| Zitat von csde_rats
Einer für alles und alle für einen ist aber eher weniger tolles Softwaredesign. Und PHP ist schließlich nicht nur Software, sondern gar Sprache, wo herausragendes Design viel viel wichtiger ist, weil schlechtes Sprachdesign direkt in die Software durchschlägt.
| |
Da will ich auch gar nicht wiedersprechen, der Grund dafür liegt wohl einfach darin wie PHP entstanden ist und wie es sich von dort an weiterentwickelt hat.
|
|
|
|
|
|
|
| Zitat von csde_rats
Vorteil is halt viel schönere Configfiles und die Dokumentation is durchgehend im Quelltext. die README.md wird von i3pystatus.mkdocs erzeugt…
| |
Also an dem Code ist mal _rein gar nichts_ schön
|
|
|
|
|
|
|
Das Hauptmodul is halt hacky, weil das alle Hacks aus allen anderen Modulen abzieht und bündelt
Insbesondere so Methoden die "einfach das richtige" machen sollen, werden halt schnell unschön, weil man dann doch Typen prüfen muss, um herauszufinden was verlangt wird.
(Vermutlich zerteile ich aber eh mal das Hauptmodul, weil da einfach zu viel nicht zusammengehöriges Zeug zusammen in einer Datei steht.)
Im Gegenzug sind die eigentlichen Module schön sauber und einfach gehalten. Beispiel: notmuch-Backend für E-Mails nachschauen:
|
Code: |
class NotmuchMailChecker(Backend):
"""
This class uses the notmuch python bindings to check for the
number of messages in the notmuch database with the tags "inbox"
and "unread"
"""
settings = required = ("db_path",)
def init(self):
self.db = notmuch.Database(self.db_path)
@property
def unread(self):
return notmuch.Query(self.db, "tag:unread and tag:inbox").count_messages()
|
|
|
|
|
|
|
|
|
Und die Config ist inwischen recht angenehm geworden, finde ich (imports weggelassen).
|
Code: |
status = i3pystatus(standalone=True)
status.register(clock,
format="%a %-d %b %X"
)
status.register(mail.Mail(
backends=[thunderbird.Thunderbird()],
format="@{unread}",
format_plural="@{unread}",
))
status.register(modsde,
username="csde_rats",
password="1234",
format="M{unread}",
offset=636,
)
status.register(regex,
regex="speed:\s+([0-9]+)\nlevel:\s+([a-zA-Z0-9]+)",
file="/proc/acpi/ibm/fan",
format="{0}/{1}",
)
status.register(battery)
status.run() |
|
/e:
Wenn ich halt so die Methode register habe, die auf viele verschiedene Arten aufgerufen werden kann (mit Modul oder Instanz und ggf. Parametern, dict als Parameter sowie optional der Position in allen Konstellationen), sieht die erstmal so aus:
|
Code: |
def register(self, module, position=0, *args, **kwargs):
"""Register a new module."""
module, position = self.get_instance_for_module(module, position, args, kwargs)
self.modules.append(module)
module.position = position
module.registered(self) |
|
Das gewurschtel mit den Parametern wird also erstmal wegdelegiert, an gifm, was so aussieht — hier wirds schon schmutzig und man merkt, dass meine Fähigkeit zum Bennen von Sachen langsam grenzwertig wird…
|
Code: |
def get_instance_for_module(self, module, position, args, kwargs):
if isinstance(module, types.ModuleType):
if not isinstance(position, int) and not args:
args = (position,)
position = 0
module = self.finder.instanciate_class_from_module(module, *args, **kwargs)
return (module, position) |
|
(Kommentar, Fehlerwerfen weggelassen)
Ich prüfe hier zweimal die Typen, nicht so schön, aber geht nicht wirklich anders.
Das mit position kommt direkt von dem oben geschriebenen.
self.finder ist eine spezialisierung von ClassFinder, die so aussieht:
|
Code: |
ModuleFinder = functools.partial(ClassFinder, baseclass=Module, exclude=[Module, IntervalModule, AsyncModule]) |
|
ClassFinder ist eine supportklasse, die Module nach Klassen bestimmter Basen durchsucht und instanziieren kann.
|
Code: |
class ClassFinder:
"""Support class to find classes of specific bases in a module"""
def __init__(self, baseclass, exclude=[]):
self.baseclass = baseclass
self.exclude = exclude
def predicate(self, obj):
return inspect.isclass(obj) and issubclass(obj, self.baseclass) and obj not in self.exclude
def search_module(self, module):
# Neat trick: [(x,y),(u,v)] becomes [(x,u),(y,v)]
return list(zip(*inspect.getmembers(module, self.predicate)))[1]
def get_class(self, module):
classes = self.search_module(module)
if len(classes) > 1:
# If there are multiple Module clases bundled in one module,
# well, we can't decide for the user.
raise ConfigAmbigiousClassesError(module.__name__, classes)
elif not classes:
raise ConfigInvalidModuleError(module.__name__)
return classes[0]
def instanciate_class_from_module(self, module, *args, **kwargs):
return self.get_class(module)(*args, **kwargs) |
|
Das ist halt… meh. Ich wüsste nicht, wie ich das anders machen sollte.
SettingsBase sieht auch kacke aus. Auch wieder, weil es mit verschiedenen Eingabeformaten klarkommen muss.
|
Code: |
class SettingsBase:
def __init__(self, *args, **kwargs):
def flatten_settings(settings):
return tuple((flatten_setting(setting) for setting in settings))
def flatten_setting(setting):
return setting[0] if isinstance(setting, tuple) else setting
self.settings = flatten_settings(self.settings)
required = set()
self.required = set(self.required)
if len(args) == 1 and not kwargs:
# User can also pass in a dict for their settings
# Note: you could do that anyway, with the ** syntax
kwargs = args[0]
for key, value in kwargs.items():
if key in self.settings:
setattr(self, key, value)
required.add(key)
else:
raise ConfigKeyError(type(self).__name__, key=key)
# Some nice set magic :-) [that's more efficient if we have classes with a few thousand settings]
required &= set(self.required)
if len(required) != len(self.required):
raise ConfigMissingError(type(self).__name__, self.required-required)
self.__name__ = "{}.{}".format(self.__module__, self.__class__.__name__)
self.init()
|
|
DAS ist natürlich ein echt fetter broken, man kann problemlos nen Haufen Code smells sehen. Damit bin ich so auch nicht zufrieden.
Ich hab mich an die Klasse auch noch nich rangesetzt und da was dran getan. Vermutlich zerteile ich die etwas in ein paar enclosed methods.
|
[Dieser Beitrag wurde 2 mal editiert; zum letzten Mal von csde_rats am 23.02.2013 23:18]
|
|
|
|
|
|
: pokerface :
Von mir aus kann das da aussterben
|
[Dieser Beitrag wurde 1 mal editiert; zum letzten Mal von Gore am 23.02.2013 23:37]
|
|
|
|
|
|
mein code für heute. habe zum ersten mal einen designfehler in Qt entdeckt: man kann die hintergrundfarbe von tooltips nicht per stylesheet, sondern nur per palette ändern. da die jedoch global ist, muss man sie resetten. da sich schließende tooltips kein signal aussenden, muss man pollen
|
|
|
|
|
|
|
|
|
|
|
Kate, vermute ich mal stark
|
|
|
|
|
|
|
| Zitat von Redh3ad
Kate, vermute ich mal stark
| | richtig. seit kde 4.10 mit python erweiterbar. die ipython-konsole unten war einfacher einzubauen als der doofe farb-tooltip dank dem beschriebenen fehldesign
aber kate ist richtig geil. man kann KDE-typisch ALLES anpassen. das unten sind ausklappbare toolboxen, z.b. ein teminal, eine python-konsole mit zugriff auf den editor selbst, ein xml-validator, globales search/replace, …
die UI, die ich hier hab ist ja schon recht minimal. man kann dann noch die tab-leiste, zeilennummern und code-folding ausblenden, die minimap duch eine scollleiste ersetzen und alle tools unten bis auf „dokumente“ deaktivieren. oder andersrum eine statusleiste einblenden, ein nicht-globales menü nutzen und ein tool rechts, eins links, und eins oben hinverschieben und an allen 4 seiten eine toolbox einblenden.
|
|
|
|
|
|
|
Ist nesting für dock-widgets an? fakeedit: ich glaube nicht. Schade. Das ist sonst sehr toll, bei kdenlive geht das z.B.. Das ist im Grunde ein eigener Tiling-WM… ein auch ziemlich mächtiger Tiling-WM. So gesehen vermutlich auch der beste, den es für Windows gibt.
|
|
|
|
|
|
|
Dinge, die ein cms sagt, wenn seine Freundin ihm in die Seite tritt, weil er zu laut schnarcht: "Und der Hermelin ist auch da!"
...
Watt?
|
|
|
|
|
|
|
Hat sie sich in letzter Zeit vielleicht nicht so sehr rasiert?
|
|
|
|
|
|
|
es heißt "das Hermelin"
|
|
|
|
|
|
|
Ja, das ist korrekt. Aber im Semi-Tiefschlaf ist korrekte Grammatik nur ein behinderndes Konstrukt!
Könnte natürlich auch sein, dass ich "Und der Herr Melin ist auch da" gesagt habe, aber ... nein!
|
[Dieser Beitrag wurde 1 mal editiert; zum letzten Mal von cms am 24.02.2013 15:12]
|
|
|
|
|
|
| Das Hermelin, genannt auch Großes Wiesel oder Kurzschwanzwiesel… | |
Netter Python-Trick von vor ein paar Tagen:
zip(*[ [a,b], [c,d], [e,f] ]) = [ [a,c,e], [b,d,f] ]
Find ich ganz nützlich, wenn man irgendwoher eine Liste von n-tupeln bekommt und man sozusagn eine Spalte haben will.
|
[Dieser Beitrag wurde 1 mal editiert; zum letzten Mal von csde_rats am 24.02.2013 21:09]
|
|
|
|
|
|
Ohne ein IColumnMajorOrderTransposeConverterFactory -Interface per DI reinzuholen? Sie MONSTER!
|
|
|
|
|
|
Thema: Gehirnsalat ( wir unter uns ) |
|