动态选择/实例化一个类

时间:2020-05-17 12:30:15

标签: python oop sqlalchemy factory-pattern factory-method

我正在设法弄清楚“工厂模式”的思想-将类的创建抽象为一个辅助函数。我想获得有关设计注意事项的反馈,以查看是否可以改进或简化它。目前,我只有几个班级,但是我想要一个可以扩展到更多的模式。

有人建议在classFactory()函数中使用class_ = getattr(sys.modules[__name__], class_name)不好,因为它太深奥了。他们建议我应该在班上有一些方法来执行此任务。我想这将意味着很多样板,总是对我的可读性造成损害。

是否正在动态实例化人们在实践中正在做的事情?我做她的方式是否可以接受,您会被烦恼地在要求您工作的代码库中找到它吗?

我认为每种类型的基数都有一个classFactory()函数。此处显示的是处理“解剖”的。

干杯

安德鲁

from sqlalchemy import Integer, Column, String, ForeignKey
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relation

# For error handling
from sqlalchemy.orm.exc import NoResultFound
from sqlalchemy.orm.exc import MultipleResultsFound

import sys

Base = declarative_base()


# Genus is currently the "parent" of everything. It is the "root".
class Genus(Base):
    __tablename__ = 'genus'
    id = Column(Integer, primary_key=True)
    scientific_name = Column(String)

    def __repr__(self):
        return "<Genus(common_name='%s')>" % (self.scientific_name)


# Species is a child of Genus
class Species(Base):
    __tablename__ = 'species'
    id = Column(Integer, primary_key=True)
    common_name = Column(String)
    scientific_name = Column(String)
    # We define the relationship between Genus and Species here.
    genus = relation("Genus", backref="species")
    genus_id = Column(Integer, ForeignKey('genus.id'), nullable=False)

    def __repr__(self):
        return "<Species(common_name='%s')>" % (self.scientific_name)


class Specimen(Base):

    __tablename__ = 'specimen'
    name = Column(String, primary_key=True)
    birth_date_time = Column(Integer)
    # We define the relationship between Species and Specimen here.
    species = relation("Species", backref="specimen")
    species_id = Column(Integer, ForeignKey('species.id'))

    def __repr__(self):
        return "<Specimen(common_name='%s')>" % (self.name)


"""
This factory function will return any type of Class. We tell it what type of class we want with class_ name and give it some input data.
We use this "factory function" because we will need a LOT less code in our addSpecies() and addSpecimen() functions.
This function expects the following inputs:
session: The sqlalchemy database session.
data: A dict containing data we want to retreive or put in to the database
class_name: Which class do we want to instanciate? Genus, Species or Specimen?
Search key: When we try and get a record from the database, which key/column are we searching?

The output is a class instance. A record fetched from the database with session.query or one just added to the database with session.add.
"""
def classFactory(session, input_data, class_name, search_key):
    # Create a temporary referance to our class. "Dynamic Instanciation" Class could now be Genus, Species or Specimen
    # https://stackoverflow.com/questions/4821104/dynamic-instantiation-from-string-name-of-a-class-in-dynamically-imported-module
    class_ = getattr(sys.modules[__name__], class_name)
    try:
        # We see if there is already a record in the database. 
        # We use getattr because we are parameterising the instance variables. 
        # class_instance = session.query(Genus).filter(Genus.scientific_name == species_input["genus"]).one()
        class_instance = session.query(class_).filter(getattr(class_, search_key) == input_data[search_key]).one()
        print("Result Found")
        return class_instance
    except NoResultFound:  # Will Run if no record found in the database.
        print("No Result Found")
        class_instance = class_()  # Create a class instance        
        # Iterate over our input_data adding the data to the class instance. 
        # It is important that the keys in the dict match the class attributes.
        for key, value in input_data.items():
            setattr(class_instance, key, value)  # set the class attribute.
        session.add(class_instance)
        session.commit()
        return class_instance


def addSpecies(session, species_input):
    # Species_input should be a dict like:
    # {"common_name": "Domestic Cat", "scientific_name": "Felis catus", "genus": "Felis"}
    species_input["genus"] = classFactory(session, species_input["genus"], "Genus", "scientific_name")
    # species_input["genus"] has now been converted into a Genus class instance.
    species = classFactory(session, species_input, "Species", "scientific_name")


def addSpecimen(session, specimen_input):
    # specimen_input should be a dict like:
    # {"name": "bongo", "species": {"scientific_name": "Felis nigripes"}, "birth_date_time": "1262304000"}
    specimen_input["species"] = classFactory(session, specimen_input["species"], "Species", "scientific_name")
    # specimen_input["species"] has been converted to a Species class instance.
    specimen = classFactory(session, specimen_input, "Specimen", "name")


""" 
def addSpecimen(session, specimen_input):
    try:  # Lets check if we can retreive the species matching the specimen record.
        species = session.query(Species).filter(Species.scientific_name == specimen_input["scientific_species"]).one()
        try:  # We see if there is already a record in the database.
            print("#####START#####")
            print(specimen_input["name"])
            specimen = session.query(Specimen).filter(Specimen.name == specimen_input["name"]).one()
            print("Record for Specimen Found: " + specimen.name)
        except NoResultFound:  # Run if no record found in the database.
            print("Specimen not found in Database: Trying to insert record")
            specimen = Specimen()
            specimen.name = specimen_input["name"]
            specimen.birth_date_time = specimen_input["birth_date_time"]
            specimen.species = species
            session.add(specimen)
        session.commit()
    except NoResultFound: # We didn't find a matching species. Freak out.
        print("Fatal Error: Species not found in Database")
        exit()
    session.commit()
"""

if __name__ == "__main__":
    from sqlalchemy import create_engine
    from sqlalchemy.orm import sessionmaker
    import os
    # Delete the database that might already exist.
    try:
        os.remove("foos.db")
    except:
        pass


    # Define a list of dicts of our species.
    species_list = [
        {"common_name": "Domestic Cat", "scientific_name": "Felis catus", "genus": {"scientific_name": "Felis"} },
        {"common_name": "Black Footed cat", "scientific_name": "Felis nigripes", "genus": {"scientific_name": "Felis"}},
        {"common_name": "Jungle cat", "scientific_name": "Felis chaus", "genus": {"scientific_name": "Felis"}},
        {"common_name": "Domestic Dog", "scientific_name": "Canis familiaris", "genus": {"scientific_name": "Canis"}},
        {"common_name": "Elk", "scientific_name": "Cervus canadensis", "genus": {"scientific_name": "Cervus"}},
        {"common_name": "Giant amoeba", "scientific_name": "Chaos carolinense", "genus": {"scientific_name": "Chaos"}},
        {"common_name": "Common Bottlenose Dolphin", "scientific_name": "Tursiops truncatus", "genus": {"scientific_name": "Tursiops"}},
        {"common_name": "Sea otter", "scientific_name": "Enhydra lutris", "genus": {"scientific_name": "Enhydra"}},
        {"common_name": "Wolf", "scientific_name": "Canis lupus", "genus": {"scientific_name": "Canis"}}
    ] 
    # Define a list of dicts of our specimens.
    specimen_list = [
        {"name": "bongo", "species": {"scientific_name": "Felis nigripes"}, "birth_date_time": "1262304000"},
        {"name": "coco", "species": {"scientific_name": "Cervus canadensis"}, "birth_date_time": "1293840000"},
        {"name": "lola", "species": {"scientific_name": "Tursiops truncatus"}, "birth_date_time": "1325376000"},
        {"name": "shadow", "species": {"scientific_name": "Tursiops truncatus"}, "birth_date_time": "1356998400"},
        {"name": "stella", "species": {"scientific_name": "Enhydra lutris"}, "birth_date_time": "1420070400"}
    ]

    # A bunch of stuff to make the connection to the database work.
    engine = create_engine('sqlite:///foos.db', echo=False)
    Base.metadata.create_all(engine)
    Session = sessionmaker(bind=engine)
    session = Session()

    # Iterate over our list of dicts.
    for species in species_list:
        addSpecies(session, species)

    for specimen in specimen_list:
        addSpecimen(session, specimen)

0 个答案:

没有答案