1

Python3

Tried to found an answer but failed. First I'll present the snippet, then I'll explain why I wanted to do it this way and what I wanted to achieve. Maybe it'll look like this approach is "the bad one". Hence this semi-double topic, cause first I'd like to know why this snippet isn't working and second - I'd like to know if this approach is right.

So:

class Namespace:
    def some_function():
        pass

    class SomeClass:
        fcnt = some_function

This won't work due to:

NameError: name 'some_function' is not defined

What I want to achieve is code and file structure readability.

Above example is a snippet which I use (not this one, but it looks like this) in Pyramid project.

My project tree looks like this:

my_project
├── models
│   ├── __init__.py
│   └── some_model.py
├── schemas
│   ├── __init__.py
│   ├── some_schema.py
│   └── some_other_schema.py
...
├── views
│   ├── __init__.py
│   └── some_view.py
└── __init__.py

What I wanted to achieve is clean schema/model/view importing.

In some_schema.py file resides class SomeSchema, in some_other_schema.py class SomeOtherSchema.

With above snippet I can make: from my_project.schemas.some_schema import Schema

and use it like Schema.SomeSchema()

I've got a little bit lost with packages and imports. How one could make a clean structure (one schema per file) and still be able to use Schema namespace? (In C++ I'd just put each of those classes in Schema namespace, that's why I did this in snippet above. But! What works in C++ maybe shouldn't be used in python, right?).

Thanks for answer in advance.

EDIT: Ok, I've done some testing (I thought that I've done it, but looks like not..).

  1. using from my_project.schemas.some_schema import Schema with another from my_project.schemas.some_other_schema import Schema causes in the second import shadowing first one. So if after first import I'd be able to use x = Schema.SomeSchema() than after second import I'd be unable to do this, because class Schema gets overriden. Right, so as Erik said - classes aren't namespaces. GOT IT!
  2. in my very first snippet yes, I should've used fnct = Namespace.some_function. What's wierd - it works. I have the same statement in my pyramid code, with one difference. some_function has @colander.deferred decorator. In fact it looks like this:

    class Schema:
        @colander.deferred
        def deferred_some_function(node, kw):
            something  = kw.get("something", [])
            return deform.widget.SelectWidget(values=something,
                                          multiple=True)
    
        class SomeSchema(colander.MappingSchema):
            somethings  = colander.SchemaNode(colander.Set(),
                                          widget=Schema.deferred_some_function)
    

    And I get NameError: name 'Schema' is not defined

  3. Getting back to package format. With this:

    ### another/file.py
    from foo.bar.schema import SomeSchema
    
    # do something with SomeSchema:
    smth = SomeSchema()
    smth.fcnt()
    

    I have to make one module foo/bar/schema.py in which I'd have to put all my SomeXSchema classes. An if I have lots of them, then there's the unreadabilty glitch which I wanted to get rid off by splitting SomeXSchema - one per file. Can I accomplish this somehow? I want to call this class for example: User. And here's the THING. Maybe I do it wrong? I'd like to have class named User in schema namespace and class named User in model namespace. Shouldn't I? Maybe I ought to use prefix? Like class SchemaUser and class ModelUser ? I wanted to avoid it by the use of modules/packages. If I'd use : import foo.bar.schema then I'd have to use it like x = foo.bar.schema.User() right? There is no way to use it like x = schema.User() ? Sorry, I just got stuck, my brain got fixed. Maybe I need a little break to take a fresh look?

    ANOTHER EDIT (FOR POINT 3 ONLY)

    I did some more research. The answer here would be to make it like this:

    ## file: myproject/schemas/__init__.py
    from .some_schema import SomeSchema
    from .some_other_schema import SomeOtherSchema
    

    then usage would be like this:

    ## some file using it
    import myproject.schemas as schema
    s1 = schema.SomeSchema()
    s2 = schema.SomeOtherSchema()
    

    Would it be lege artis?

  4. If anyone thinks that topic should be changed - go ahead, give me something more meaningful, I'd appreciate it.

jaor
  • 831
  • 7
  • 18
  • Executing the snippet gives me `NameError: name 'some_function' is not defined` and not the error you point out. – Erik Kaplun Oct 26 '13 at 08:00
  • why are you making this more complicated that it needs to be? I've provided you with 2 alternative importing styles, one that can causes shadowing and one that doesn't. Also, please note that according to PEP8, module names should be `some_schema` or `someschema` but not `someSchema`. – Erik Kaplun Oct 26 '13 at 14:31
  • I've edited my answer to reflect your edits. – Erik Kaplun Oct 26 '13 at 14:37
  • Edited examples to be ok with PEP8. Thanks for pointing that out. – jaor Oct 26 '13 at 14:54
  • about your point 4.—topics should not be changed; the question title sets the topic and unless you edit the title and the question, the topic cannot be changed without going off-topic. – Erik Kaplun Oct 27 '13 at 11:08

2 Answers2

2

Your are swimming upstream by trying to do what you are trying to do.

Classes are meant for defining new data types not as a means to group related parts of code together. Modules are perfectly suited for that, and I presume you know that well because of the "(vs proper package structure)" part in the question title.

Modules can also be imported as objects, so to achieve what you want:

### foo/bar/schema.py
def some_function():
    pass
class SomeSchema:
    fcnt = some_function


### another/file.py
from foo.bar import schema

# do something with SomeSchema:
smth = schema.SomeSchema()
smth.fcnt()

...although it's also typical to import classes directly into the scope like this (i.e. being able to refer to SomeSchema after the import as opposed to schema.SomeSchema):

### another/file.py
from foo.bar.schema import SomeSchema

# do something with SomeSchema:
smth = SomeSchema()
smth.fcnt()

(Also note that module names should be lowercase as suggested by PEP8 and only class names should use PascalCase)

This, by the way, applies to programming in general, not just Python. There are a few languages such as Java and C# which require that functions be declared inside of classes as statics because they disallow writing of code outside of classes for some weird reason, but even these languages have modules/proper namespaces for structuring your code; i.e. classes are not normally put inside other classes (they sometimes are, but for wholly different reasons/goals than yours).

So basically "class" means a "type" or "a set of objects having similar behavior"; once you ignore that principle/definition, you're writing bad code by definition.

PS. if you are using Python 2.x, you should be inheriting your classes from object so as to get new-style classes.

PPS. in any case, even technically speaking, what you are trying to do won't work cleanly in Python:

class fake_namespace:
    def some_function():
        pass
    class RealClass:
        some_function  # <-- that name is not even visibile here;
                       # you'd have to use fake_namespace.some_function instead

...and this is the reason for the exception I reported I was getting: NameError: name 'some_function' is not defined.

EDIT AS PER YOUR EDITS:

I'm not really sure why you're making it so complicated; also some of your statements are false:

If I'd use : import foo.bar.schema then I'd have to use it like x = foo.bar.schema.User right?

No. Please learn how Python modules work.

I'd like to have class named User in Schema namespace and class named User in Model namespace. Shouldn't I? Maybe I ought to use prefix? Like class SchemaUser and class ModelUser

please note that namespaces a.k.a. modules should be lowercase not PascalCase.

An if I have lots of them, then there's the unreadabilty glitch which I wanted to get rid off by splitting SomeXSchema - one per file. Can I accomplish this somehow?

Yes; you can put your classes in individual submodules, e.g. schema1/class1.py, schema/class2.py etc; then you can "collect" them into schema/__init__.py so that you could import them directly from schema:

# schema/__init__.py
from .class1 import Class1
from .class2 import Class2

__all__ = [Class1, Class2]  # optional

General note: you can name your schema modules differently, e.g. schema1, schema2, etc; then you could just use them like this:

from somewhere import schema1
from somewhere_else import schema2

s1_user = schema1.User()
s2_user = schema2.User()
# etc

For more information on how Python modules work, refer to http://docs.python.org/2/tutorial/modules.html

Community
  • 1
  • 1
Erik Kaplun
  • 37,128
  • 15
  • 99
  • 111
  • 1. I know no other way to instantiate object of class SomeSchema residing in `foo/bar/schema.py` with this import: `import foo.bar.schema` than to do `x=foo.bar.schema.SomeSchema()` 2. PEP8 - fixing! Sorry. 3. So the idea which I wrote is ok (the one with `from .some_schema import SomeSchema`) – jaor Oct 26 '13 at 14:49
  • 1. did you miss my `from foo.bar import schema` and `from foo.bar.schema import SomeSchema`? 3. yes, why not. – Erik Kaplun Oct 27 '13 at 11:05
  • 1. It was reply to yours "No. Please learn how Python modules work." where example was using EXACTLY this import : `import foo.bar.schema`. I haven't missed anything. So to get to the point with ABOVE import statement there is no other way to use it's contents than to `x=foo.bar.schema.SomeSchema()`. 3. great! – jaor Oct 27 '13 at 11:20
0

Name and binding

You can read Python naming and binding and understand how Python namespace works.

A scope defines the visibility of a name within a block. If a local variable is defined in a block, its scope includes that block. If the definition occurs in a function block, the scope extends to any blocks contained within the defining one, unless a contained block introduces a different binding for the name. The scope of names defined in a class block is limited to the class block; it does not extend to the code blocks of methods this includes generator expressions since they are implemented using a function scope.

BTW, use globals() and locals() can help debug for variable binding.

The User Problem

You can try this instead:

from model import User as modelUser
from foo.bar.schema import User as schemaUser
Goat
  • 188
  • 7
  • I wanted to avoid using `as` in this context (`from foo.bar.schema import User as schemaUser`). That's what namespaces are for, right? I might also be "polluted" by C++, and maybe that's the "Python's way"? – jaor Oct 27 '13 at 06:04
  • Yes. In fact, using assignment in module scope means adding a attribute to the module's `namespace`. And `from model import User as modelUser` almost equals `modelUser = model.User`, they both are assignment statement. You can feel free to use it. – Goat Oct 27 '13 at 06:50
  • That's the "Python's way" IMO. :-) – Goat Oct 27 '13 at 06:57
  • 2
    @Goat: only it should be `from model import User as ModelUser`—class names should be capitalized regardless of whether the name is the original name or an alias. – Erik Kaplun Oct 27 '13 at 11:07